You've already forked hotpocket
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0c12f52569 | |||
| a6f01ba71e | |||
| 77526b1fae | |||
| 7c97445155 | |||
| a0f1a4ce80 | |||
| 80fbbcddf3 | |||
| 0ab87e25a4 | |||
| 495255206e | |||
| 46254730bd | |||
| d1e60babf4 | |||
| ab84f685c0 | |||
| 1a8c4bfebc | |||
| b15b48f702 |
@@ -66,7 +66,7 @@ $ docker run --rm -it \
|
||||
-e HOTPOCKET_BACKEND_INITIAL_ACCOUNT_USERNAME=hotpocket \
|
||||
-e HOTPOCKET_BACKEND_INITIAL_ACCOUNT_PASSWORD=hotpocketm4st3r \
|
||||
-p 8000:8000 \
|
||||
docker-hosted.nexus.bthlabs.pl/hotpocket/backend:aio-v25.9.12-01
|
||||
docker-hosted.nexus.bthlabs.pl/hotpocket/backend:aio-v25.9.18-01
|
||||
```
|
||||
|
||||
The command above will set up and start the application. The SQLite file will
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
backend:
|
||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:aio-v25.9.12-01"
|
||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:aio-v25.9.18-01"
|
||||
environment:
|
||||
HOTPOCKET_BACKEND_SECRET_KEY: "thisisntright"
|
||||
HOTPOCKET_BACKEND_INITIAL_ACCOUNT_USERNAME: "hotpocket"
|
||||
|
||||
@@ -8,7 +8,7 @@ x-backend-environment: &x-backend-environment
|
||||
|
||||
services:
|
||||
webapp:
|
||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.9.12-01"
|
||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.9.18-01"
|
||||
environment:
|
||||
<<: *x-backend-environment
|
||||
HOTPOCKET_BACKEND_ALLOWED_HOSTS: "app.staging.hotpocket.bthlab.bthlabs.net"
|
||||
@@ -21,7 +21,7 @@ services:
|
||||
restart: "unless-stopped"
|
||||
|
||||
admin:
|
||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.9.12-01"
|
||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.9.18-01"
|
||||
environment:
|
||||
<<: *x-backend-environment
|
||||
HOTPOCKET_BACKEND_APP: "admin"
|
||||
@@ -35,7 +35,7 @@ services:
|
||||
restart: "unless-stopped"
|
||||
|
||||
celery-worker:
|
||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.9.12-01"
|
||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.9.18-01"
|
||||
command:
|
||||
- "/srv/venv/bin/celery"
|
||||
- "-A"
|
||||
@@ -57,7 +57,7 @@ services:
|
||||
restart: "unless-stopped"
|
||||
|
||||
celery-beat:
|
||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.9.12-01"
|
||||
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:deployment-v25.9.18-01"
|
||||
command:
|
||||
- "/srv/venv/bin/celery"
|
||||
- "-A"
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"run": {
|
||||
"echo": true,
|
||||
"pty": true
|
||||
}
|
||||
}
|
||||
8
invoke.yaml
Normal file
8
invoke.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
run:
|
||||
echo: true
|
||||
pty: true
|
||||
files_to_version:
|
||||
- "deployment/aio/docker-compose.yaml"
|
||||
- "deployment/fullstack/docker-compose.yaml"
|
||||
- "pyproject.toml"
|
||||
- "README.md"
|
||||
3
poetry.lock
generated
3
poetry.lock
generated
@@ -9,6 +9,9 @@ python-versions = "^3.12"
|
||||
files = []
|
||||
develop = true
|
||||
|
||||
[package.dependencies]
|
||||
invoke = "2.2.0"
|
||||
|
||||
[package.source]
|
||||
type = "directory"
|
||||
url = "services/packages/workspace_tools"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "hotpocket-workspace"
|
||||
version = "1.0.0"
|
||||
version = "25.9.18"
|
||||
description = "HotPocket Workspace"
|
||||
authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
2
services/apple/.gitignore
vendored
2
services/apple/.gitignore
vendored
@@ -90,3 +90,5 @@ Shared (Extension)/Resources/images/
|
||||
Shared (Extension)/Resources/background-bundle.js
|
||||
Shared (Extension)/Resources/content-bundle.js
|
||||
Shared (Extension)/Resources/manifest.json
|
||||
Shared (Extension)/Resources/preauth.html
|
||||
Shared (Extension)/Resources/preauth.js
|
||||
|
||||
@@ -114,6 +114,8 @@
|
||||
"Resources/content-bundle.js",
|
||||
Resources/images,
|
||||
Resources/manifest.json,
|
||||
Resources/preauth.html,
|
||||
Resources/preauth.js,
|
||||
SafariWebExtensionHandler.m,
|
||||
);
|
||||
target = 4CABCAD42E56F0C900D8A354 /* HotPocket Extension (iOS) */;
|
||||
@@ -126,6 +128,8 @@
|
||||
"Resources/content-bundle.js",
|
||||
Resources/images,
|
||||
Resources/manifest.json,
|
||||
Resources/preauth.html,
|
||||
Resources/preauth.js,
|
||||
SafariWebExtensionHandler.m,
|
||||
);
|
||||
target = 4CABCADE2E56F0C900D8A354 /* HotPocket Extension (macOS) */;
|
||||
@@ -462,7 +466,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2025091201;
|
||||
CURRENT_PROJECT_VERSION = 2025091701;
|
||||
DEVELOPMENT_TEAM = 648728X64K;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "iOS (Extension)/Info.plist";
|
||||
@@ -474,7 +478,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 25.9.12;
|
||||
MARKETING_VERSION = 25.9.17;
|
||||
OTHER_LDFLAGS = (
|
||||
"-framework",
|
||||
SafariServices,
|
||||
@@ -492,7 +496,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2025091201;
|
||||
CURRENT_PROJECT_VERSION = 2025091701;
|
||||
DEVELOPMENT_TEAM = 648728X64K;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "iOS (Extension)/Info.plist";
|
||||
@@ -504,7 +508,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 25.9.12;
|
||||
MARKETING_VERSION = 25.9.17;
|
||||
OTHER_LDFLAGS = (
|
||||
"-framework",
|
||||
SafariServices,
|
||||
@@ -525,7 +529,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2025091201;
|
||||
CURRENT_PROJECT_VERSION = 2025091701;
|
||||
DEVELOPMENT_TEAM = 648728X64K;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "iOS (App)/Info.plist";
|
||||
@@ -541,7 +545,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 25.9.12;
|
||||
MARKETING_VERSION = 25.9.17;
|
||||
OTHER_LDFLAGS = (
|
||||
"-framework",
|
||||
SafariServices,
|
||||
@@ -566,7 +570,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2025091201;
|
||||
CURRENT_PROJECT_VERSION = 2025091701;
|
||||
DEVELOPMENT_TEAM = 648728X64K;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "iOS (App)/Info.plist";
|
||||
@@ -582,7 +586,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 25.9.12;
|
||||
MARKETING_VERSION = 25.9.17;
|
||||
OTHER_LDFLAGS = (
|
||||
"-framework",
|
||||
SafariServices,
|
||||
@@ -608,7 +612,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/HotPocket.entitlements";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2025091201;
|
||||
CURRENT_PROJECT_VERSION = 2025091701;
|
||||
DEVELOPMENT_TEAM = 648728X64K;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
@@ -622,7 +626,7 @@
|
||||
"@executable_path/../../../../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MARKETING_VERSION = 25.9.12;
|
||||
MARKETING_VERSION = 25.9.17;
|
||||
OTHER_LDFLAGS = (
|
||||
"-framework",
|
||||
SafariServices,
|
||||
@@ -641,7 +645,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/HotPocket.entitlements";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2025091201;
|
||||
CURRENT_PROJECT_VERSION = 2025091701;
|
||||
DEVELOPMENT_TEAM = 648728X64K;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
@@ -655,7 +659,7 @@
|
||||
"@executable_path/../../../../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MARKETING_VERSION = 25.9.12;
|
||||
MARKETING_VERSION = 25.9.17;
|
||||
OTHER_LDFLAGS = (
|
||||
"-framework",
|
||||
SafariServices,
|
||||
@@ -676,7 +680,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "macOS (App)/HotPocket.entitlements";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2025091201;
|
||||
CURRENT_PROJECT_VERSION = 2025091701;
|
||||
DEVELOPMENT_TEAM = 648728X64K;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -690,7 +694,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||
MARKETING_VERSION = 25.9.12;
|
||||
MARKETING_VERSION = 25.9.17;
|
||||
OTHER_LDFLAGS = (
|
||||
"-framework",
|
||||
SafariServices,
|
||||
@@ -713,7 +717,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "macOS (App)/HotPocket.entitlements";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2025091201;
|
||||
CURRENT_PROJECT_VERSION = 2025091701;
|
||||
DEVELOPMENT_TEAM = 648728X64K;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -727,7 +731,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 15.0;
|
||||
MARKETING_VERSION = 25.9.12;
|
||||
MARKETING_VERSION = 25.9.17;
|
||||
OTHER_LDFLAGS = (
|
||||
"-framework",
|
||||
SafariServices,
|
||||
|
||||
3
services/apple/README.md
Normal file
3
services/apple/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# HotPocket by BTHLabs
|
||||
|
||||
This repository contains the _HotPocket Apple Integrations_ project.
|
||||
5
services/apple/invoke.yaml
Normal file
5
services/apple/invoke.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
run:
|
||||
echo: true
|
||||
pty: true
|
||||
files_to_version:
|
||||
- "HotPocket.xcodeproj/project.pbxproj"
|
||||
511
services/apple/poetry.lock
generated
Normal file
511
services/apple/poetry.lock
generated
Normal file
@@ -0,0 +1,511 @@
|
||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "asttokens"
|
||||
version = "3.0.0"
|
||||
description = "Annotate AST trees with source code positions"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"},
|
||||
{file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
astroid = ["astroid (>=2,<4)"]
|
||||
test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"]
|
||||
|
||||
[[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"
|
||||
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 = "decorator"
|
||||
version = "5.2.1"
|
||||
description = "Decorators for Humans"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"},
|
||||
{file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "executing"
|
||||
version = "2.2.1"
|
||||
description = "Get the currently executing AST node of a frame, and other information"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017"},
|
||||
{file = "executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"]
|
||||
|
||||
[[package]]
|
||||
name = "factory-boy"
|
||||
version = "3.3.3"
|
||||
description = "A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "factory_boy-3.3.3-py2.py3-none-any.whl", hash = "sha256:1c39e3289f7e667c4285433f305f8d506efc2fe9c73aaea4151ebd5cdea394fc"},
|
||||
{file = "factory_boy-3.3.3.tar.gz", hash = "sha256:866862d226128dfac7f2b4160287e899daf54f2612778327dd03d0e2cb1e3d03"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
Faker = ">=0.7.0"
|
||||
|
||||
[package.extras]
|
||||
dev = ["Django", "Pillow", "SQLAlchemy", "coverage", "flake8", "isort", "mongoengine", "mongomock", "mypy", "tox", "wheel (>=0.32.0)", "zest.releaser[recommended]"]
|
||||
doc = ["Sphinx", "sphinx-rtd-theme", "sphinxcontrib-spelling"]
|
||||
|
||||
[[package]]
|
||||
name = "faker"
|
||||
version = "37.6.0"
|
||||
description = "Faker is a Python package that generates fake data for you."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "faker-37.6.0-py3-none-any.whl", hash = "sha256:3c5209b23d7049d596a51db5d76403a0ccfea6fc294ffa2ecfef6a8843b1e6a7"},
|
||||
{file = "faker-37.6.0.tar.gz", hash = "sha256:0f8cc34f30095184adf87c3c24c45b38b33ad81c35ef6eb0a3118f301143012c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
tzdata = "*"
|
||||
|
||||
[[package]]
|
||||
name = "flake8"
|
||||
version = "7.3.0"
|
||||
description = "the modular source code checker: pep8 pyflakes and co"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e"},
|
||||
{file = "flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
mccabe = ">=0.7.0,<0.8.0"
|
||||
pycodestyle = ">=2.14.0,<2.15.0"
|
||||
pyflakes = ">=3.4.0,<3.5.0"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-commas"
|
||||
version = "4.0.0"
|
||||
description = "Flake8 lint for trailing commas."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "flake8_commas-4.0.0-py3-none-any.whl", hash = "sha256:cad476d71ba72e8b941a8508d5b9ffb6b03e50f7102982474f085ad0d674b685"},
|
||||
{file = "flake8_commas-4.0.0.tar.gz", hash = "sha256:a68834b42a9a31c94ca790efe557a932c0eae21a3479c6b9a23c4dc077e3ea96"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
flake8 = ">=5"
|
||||
|
||||
[[package]]
|
||||
name = "hotpocket-workspace-tools"
|
||||
version = "1.0.0.dev0"
|
||||
description = "HotPocket Workspace Tools"
|
||||
optional = false
|
||||
python-versions = "^3.12"
|
||||
files = []
|
||||
develop = true
|
||||
|
||||
[package.dependencies]
|
||||
invoke = "2.2.0"
|
||||
|
||||
[package.source]
|
||||
type = "directory"
|
||||
url = "../packages/workspace_tools"
|
||||
|
||||
[[package]]
|
||||
name = "invoke"
|
||||
version = "2.2.0"
|
||||
description = "Pythonic task execution"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "invoke-2.2.0-py3-none-any.whl", hash = "sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820"},
|
||||
{file = "invoke-2.2.0.tar.gz", hash = "sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipdb"
|
||||
version = "0.13.13"
|
||||
description = "IPython-enabled pdb"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
files = [
|
||||
{file = "ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4"},
|
||||
{file = "ipdb-0.13.13.tar.gz", hash = "sha256:e3ac6018ef05126d442af680aad863006ec19d02290561ac88b8b1c0b0cfc726"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
decorator = {version = "*", markers = "python_version >= \"3.11\""}
|
||||
ipython = {version = ">=7.31.1", markers = "python_version >= \"3.11\""}
|
||||
|
||||
[[package]]
|
||||
name = "ipython"
|
||||
version = "9.3.0"
|
||||
description = "IPython: Productive Interactive Computing"
|
||||
optional = false
|
||||
python-versions = ">=3.11"
|
||||
files = [
|
||||
{file = "ipython-9.3.0-py3-none-any.whl", hash = "sha256:1a0b6dd9221a1f5dddf725b57ac0cb6fddc7b5f470576231ae9162b9b3455a04"},
|
||||
{file = "ipython-9.3.0.tar.gz", hash = "sha256:79eb896f9f23f50ad16c3bc205f686f6e030ad246cc309c6279a242b14afe9d8"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
decorator = "*"
|
||||
ipython-pygments-lexers = "*"
|
||||
jedi = ">=0.16"
|
||||
matplotlib-inline = "*"
|
||||
pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""}
|
||||
prompt_toolkit = ">=3.0.41,<3.1.0"
|
||||
pygments = ">=2.4.0"
|
||||
stack_data = "*"
|
||||
traitlets = ">=5.13.0"
|
||||
|
||||
[package.extras]
|
||||
all = ["ipython[doc,matplotlib,test,test-extra]"]
|
||||
black = ["black"]
|
||||
doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinx_toml (==0.0.4)", "typing_extensions"]
|
||||
matplotlib = ["matplotlib"]
|
||||
test = ["packaging", "pytest", "pytest-asyncio (<0.22)", "testpath"]
|
||||
test-extra = ["curio", "ipykernel", "ipython[test]", "jupyter_ai", "matplotlib (!=3.2.0)", "nbclient", "nbformat", "numpy (>=1.23)", "pandas", "trio"]
|
||||
|
||||
[[package]]
|
||||
name = "ipython-pygments-lexers"
|
||||
version = "1.1.1"
|
||||
description = "Defines a variety of Pygments lexers for highlighting IPython code."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c"},
|
||||
{file = "ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pygments = "*"
|
||||
|
||||
[[package]]
|
||||
name = "isort"
|
||||
version = "6.0.1"
|
||||
description = "A Python utility / library to sort Python imports."
|
||||
optional = false
|
||||
python-versions = ">=3.9.0"
|
||||
files = [
|
||||
{file = "isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615"},
|
||||
{file = "isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
colors = ["colorama"]
|
||||
plugins = ["setuptools"]
|
||||
|
||||
[[package]]
|
||||
name = "jedi"
|
||||
version = "0.19.2"
|
||||
description = "An autocompletion tool for Python that can be used for text editors."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"},
|
||||
{file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
parso = ">=0.8.4,<0.9.0"
|
||||
|
||||
[package.extras]
|
||||
docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"]
|
||||
qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
|
||||
testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "matplotlib-inline"
|
||||
version = "0.1.7"
|
||||
description = "Inline Matplotlib backend for Jupyter"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"},
|
||||
{file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
traitlets = "*"
|
||||
|
||||
[[package]]
|
||||
name = "mccabe"
|
||||
version = "0.7.0"
|
||||
description = "McCabe checker, plugin for flake8"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
|
||||
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.16.1"
|
||||
description = "Optional static typing for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a"},
|
||||
{file = "mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72"},
|
||||
{file = "mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea"},
|
||||
{file = "mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574"},
|
||||
{file = "mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d"},
|
||||
{file = "mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4"},
|
||||
{file = "mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd"},
|
||||
{file = "mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be"},
|
||||
{file = "mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7fc688329af6a287567f45cc1cefb9db662defeb14625213a5b7da6e692e2069"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e198ab3f55924c03ead626ff424cad1732d0d391478dfbf7bb97b34602395da"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09aa4f91ada245f0a45dbc47e548fd94e0dd5a8433e0114917dc3b526912a30c"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13c7cd5b1cb2909aa318a90fd1b7e31f17c50b242953e7dd58345b2a814f6383"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:58e07fb958bc5d752a280da0e890c538f1515b79a65757bbdc54252ba82e0b40"},
|
||||
{file = "mypy-1.16.1-cp39-cp39-win_amd64.whl", hash = "sha256:f895078594d918f93337a505f8add9bd654d1a24962b4c6ed9390e12531eb31b"},
|
||||
{file = "mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37"},
|
||||
{file = "mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
mypy_extensions = ">=1.0.0"
|
||||
pathspec = ">=0.9.0"
|
||||
typing_extensions = ">=4.6.0"
|
||||
|
||||
[package.extras]
|
||||
dmypy = ["psutil (>=4.0)"]
|
||||
faster-cache = ["orjson"]
|
||||
install-types = ["pip"]
|
||||
mypyc = ["setuptools (>=50)"]
|
||||
reports = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.1.0"
|
||||
description = "Type system extensions for programs checked with the mypy type checker."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"},
|
||||
{file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parso"
|
||||
version = "0.8.5"
|
||||
description = "A Python Parser"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887"},
|
||||
{file = "parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
|
||||
testing = ["docopt", "pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.12.1"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
|
||||
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pexpect"
|
||||
version = "4.9.0"
|
||||
description = "Pexpect allows easy control of interactive console applications."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"},
|
||||
{file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
ptyprocess = ">=0.5"
|
||||
|
||||
[[package]]
|
||||
name = "prompt-toolkit"
|
||||
version = "3.0.52"
|
||||
description = "Library for building powerful interactive command lines in Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955"},
|
||||
{file = "prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
wcwidth = "*"
|
||||
|
||||
[[package]]
|
||||
name = "ptyprocess"
|
||||
version = "0.7.0"
|
||||
description = "Run a subprocess in a pseudo terminal"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
|
||||
{file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pure-eval"
|
||||
version = "0.2.3"
|
||||
description = "Safely evaluate AST nodes without side effects"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"},
|
||||
{file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
tests = ["pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "pycodestyle"
|
||||
version = "2.14.0"
|
||||
description = "Python style guide checker"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"},
|
||||
{file = "pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyflakes"
|
||||
version = "3.4.0"
|
||||
description = "passive checker of Python programs"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"},
|
||||
{file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.19.2"
|
||||
description = "Pygments is a syntax highlighting package written in Python."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"},
|
||||
{file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
windows-terminal = ["colorama (>=0.4.6)"]
|
||||
|
||||
[[package]]
|
||||
name = "stack-data"
|
||||
version = "0.6.3"
|
||||
description = "Extract data from python stack frames and tracebacks for informative displays"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"},
|
||||
{file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
asttokens = ">=2.1.0"
|
||||
executing = ">=1.2.0"
|
||||
pure-eval = "*"
|
||||
|
||||
[package.extras]
|
||||
tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"]
|
||||
|
||||
[[package]]
|
||||
name = "traitlets"
|
||||
version = "5.14.3"
|
||||
description = "Traitlets Python configuration system"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"},
|
||||
{file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
|
||||
test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.9+"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
|
||||
{file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tzdata"
|
||||
version = "2025.2"
|
||||
description = "Provider of IANA time zone data"
|
||||
optional = false
|
||||
python-versions = ">=2"
|
||||
files = [
|
||||
{file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"},
|
||||
{file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wcwidth"
|
||||
version = "0.2.13"
|
||||
description = "Measures the displayed width of unicode strings in a terminal"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
|
||||
{file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
|
||||
]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.12"
|
||||
content-hash = "1c74b6d5928a0b1bde41962f4233af2eba2242324c3155b21093b2f46baf5cd0"
|
||||
26
services/apple/pyproject.toml
Normal file
26
services/apple/pyproject.toml
Normal file
@@ -0,0 +1,26 @@
|
||||
[tool.poetry]
|
||||
name = "hotpocket-apple"
|
||||
version = "25.9.12"
|
||||
description = "HotPocket Apple Integrations"
|
||||
authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
|
||||
license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
package-mode = false
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.12"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
factory-boy = "3.3.3"
|
||||
flake8 = "7.3.0"
|
||||
flake8-commas = "4.0.0"
|
||||
hotpocket-workspace-tools = {path = "../packages/workspace_tools", develop = true}
|
||||
invoke = "2.2.0"
|
||||
ipdb = "0.13.13"
|
||||
ipython = "9.3.0"
|
||||
isort = "6.0.1"
|
||||
mypy = "1.16.1"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
16
services/apple/setup.cfg
Normal file
16
services/apple/setup.cfg
Normal file
@@ -0,0 +1,16 @@
|
||||
[flake8]
|
||||
extend-exclude =
|
||||
node_modules/**/*.py
|
||||
ignore = E131,W503,W504
|
||||
max-line-length = 119
|
||||
hang-closing = False
|
||||
|
||||
[isort]
|
||||
known_first_party=hotpocket_backend,hotpocket_backend_testing,hotpocket_common,hotpocket_soa,hotpocket_testing,hotpocket_workspace_tools
|
||||
multi_line_output=3
|
||||
include_trailing_comma=true
|
||||
force_sort_within_sections=true
|
||||
line_length=80
|
||||
use_parentheses=true
|
||||
combine_as_imports=true
|
||||
star_first=true
|
||||
124
services/apple/tasks.py
Normal file
124
services/apple/tasks.py
Normal file
@@ -0,0 +1,124 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# type: ignore
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
from invoke import Context, task
|
||||
from invoke.exceptions import UnexpectedExit
|
||||
|
||||
from hotpocket_workspace_tools import get_workspace_mode
|
||||
import hotpocket_workspace_tools.tasks
|
||||
|
||||
WORKSPACE_MODE = get_workspace_mode()
|
||||
|
||||
|
||||
@task
|
||||
def clean(ctx: Context):
|
||||
ctx.run('rm -rf DerivedData/')
|
||||
|
||||
|
||||
@task
|
||||
def test(ctx: Context):
|
||||
print('NOOP')
|
||||
|
||||
|
||||
@task
|
||||
def flake8(ctx: Context):
|
||||
ctx.run('flake8')
|
||||
|
||||
|
||||
@task
|
||||
def isort(ctx, check=False, diff=False):
|
||||
command_parts = [
|
||||
'isort',
|
||||
]
|
||||
|
||||
if check is True:
|
||||
command_parts.append('--check')
|
||||
|
||||
if diff is True:
|
||||
command_parts.append('--diff')
|
||||
|
||||
command_parts.append('.')
|
||||
|
||||
ctx.run(' '.join(command_parts))
|
||||
|
||||
|
||||
@task
|
||||
def eslint(ctx: Context):
|
||||
print('NOOP')
|
||||
|
||||
|
||||
@task
|
||||
def lint(ctx: Context):
|
||||
ihazsuccess = True
|
||||
|
||||
try:
|
||||
flake8(ctx)
|
||||
except UnexpectedExit:
|
||||
ihazsuccess = False
|
||||
|
||||
try:
|
||||
isort(ctx, check=True)
|
||||
except UnexpectedExit:
|
||||
ihazsuccess = False
|
||||
|
||||
if ihazsuccess is False:
|
||||
raise RuntimeError('FIAL')
|
||||
|
||||
|
||||
@task
|
||||
def typecheck(ctx: Context):
|
||||
ctx.run('mypy .')
|
||||
|
||||
|
||||
@task
|
||||
def django_shell(ctx: Context):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@task
|
||||
def ci(ctx: Context):
|
||||
ihazsuccess = True
|
||||
|
||||
ci_tasks = [test, lint, typecheck]
|
||||
for ci_task in ci_tasks:
|
||||
try:
|
||||
ci_task(ctx)
|
||||
except UnexpectedExit:
|
||||
ihazsuccess = False
|
||||
|
||||
if ihazsuccess is False:
|
||||
raise RuntimeError('FIAL')
|
||||
|
||||
|
||||
@task
|
||||
def setup(ctx: Context):
|
||||
print('NOOP')
|
||||
|
||||
|
||||
@task
|
||||
def start_web(ctx: Context):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@task
|
||||
def bump_version(ctx: Context, next_version: str, build: str | None = None):
|
||||
hotpocket_workspace_tools.tasks.utils.bump_version(
|
||||
ctx, next_version, build=build,
|
||||
)
|
||||
|
||||
pbxproj_content = None
|
||||
with open('HotPocket.xcodeproj/project.pbxproj', 'r', encoding='utf-8') as pbxproj_content_f:
|
||||
pbxproj_content = pbxproj_content_f.read()
|
||||
|
||||
pbxproj_content = re.sub(
|
||||
r'CURRENT_PROJECT_VERSION = [0-9]{10};',
|
||||
f'CURRENT_PROJECT_VERSION = {build};',
|
||||
pbxproj_content,
|
||||
flags=re.MULTILINE,
|
||||
)
|
||||
|
||||
with open('HotPocket.xcodeproj/project.pbxproj', 'w', encoding='utf-8') as pbxproj_content_f:
|
||||
pbxproj_content_f.write(pbxproj_content)
|
||||
@@ -7,7 +7,7 @@ export default defineConfig([
|
||||
{
|
||||
files: [
|
||||
'eslint.config.js',
|
||||
'hotpocket_backend/apps/ui/static/ui/js/hotpocket.*.js',
|
||||
'hotpocket_backend/apps/ui/static/ui/js/hotpocket-backend.*.js',
|
||||
],
|
||||
plugins: {
|
||||
js,
|
||||
@@ -66,7 +66,6 @@ export default defineConfig([
|
||||
},
|
||||
{
|
||||
ignores: [
|
||||
'hotpocket_backend/apps/**/static/**/*.js',
|
||||
'hotpocket_backend/static/**/*.js',
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
version = '25.9.12'
|
||||
version = '25.9.18'
|
||||
|
||||
@@ -21,3 +21,5 @@ class StarUnstarAssociationViewMode(enum.Enum):
|
||||
|
||||
class UIAccessTokenOriginApp(enum.Enum):
|
||||
SAFARI_WEB_EXTENSION = _('Safari Web Extension')
|
||||
CHROME_EXTENSION = _('Chrome Extension')
|
||||
FIREFOX_EXTENSION = _('Firefox Extension')
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from bthlabs_jsonrpc_core import register_method
|
||||
from django import db
|
||||
from django.http import HttpRequest
|
||||
|
||||
from hotpocket_backend.apps.ui.services.workflows import CreateSaveWorkflow
|
||||
@@ -11,11 +10,10 @@ from hotpocket_soa.dto.associations import AssociationOut
|
||||
|
||||
@register_method(method='saves.create')
|
||||
def create(request: HttpRequest, url: str) -> AssociationOut:
|
||||
with db.transaction.atomic():
|
||||
association = CreateSaveWorkflow().run_rpc(
|
||||
request=request,
|
||||
account=request.user,
|
||||
url=url,
|
||||
)
|
||||
association = CreateSaveWorkflow().run_rpc(
|
||||
request=request,
|
||||
account=request.user,
|
||||
url=url,
|
||||
)
|
||||
|
||||
return association
|
||||
return association
|
||||
|
||||
@@ -23,7 +23,6 @@ body, html {
|
||||
}
|
||||
|
||||
body > .ui-viewport {
|
||||
padding-bottom: 3.5rem;
|
||||
padding-top: 3.5rem;
|
||||
}
|
||||
|
||||
@@ -103,3 +102,23 @@ body.ui-mode-standalone #offcanvas-controls .offcanvas-body {
|
||||
#BrowseSavesView-Search .form-select {
|
||||
min-width: 10rem;
|
||||
}
|
||||
|
||||
#navbar .ui-inline-create-save-form {
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
#navbar .ui-inline-create-save-form {
|
||||
right: 0.5rem;
|
||||
width: calc(100vw - 0.5rem - 0.5rem);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
#InlineCreateSaveForm {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*!
|
||||
* HotPocket by BTHLabs (https://hotpocket.app/)
|
||||
* Copyright 2025-present BTHLabs <contact@bthlabs.pl> (https://bthlabs.pl/)
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
((HotPocket, htmx) => {
|
||||
class InlineCreateSaveForm {
|
||||
constructor (app) {
|
||||
this.app = app;
|
||||
this.element = null;
|
||||
this.buttonTrigger = null;
|
||||
this.form = null;
|
||||
}
|
||||
onLoad = (event) => {
|
||||
this.element = document.getElementById('InlineCreateSaveForm');
|
||||
this.buttonTrigger = this.element.querySelector('.ui-inline-create-save-form-trigger');
|
||||
this.buttonClose = this.element.querySelector('.ui-inline-create-save-form-close');
|
||||
this.form = this.element.querySelector('.ui-inline-create-save-form');
|
||||
|
||||
this.buttonTrigger.addEventListener('click', this.onTriggerClick);
|
||||
this.buttonClose.addEventListener('click', this.onCloseClick);
|
||||
};
|
||||
onTriggerClick = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
this.form.classList.remove('d-none');
|
||||
this.form.classList.add('d-flex');
|
||||
};
|
||||
onCloseClick = (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this.form.classList.add('d-none');
|
||||
this.form.classList.remove('d-flex');
|
||||
};
|
||||
}
|
||||
|
||||
HotPocket.addPlugin('UI.InlineCreateSaveForm', (app) => {
|
||||
return new InlineCreateSaveForm(app);
|
||||
});
|
||||
})(window.HotPocket, window.htmx);
|
||||
@@ -0,0 +1,58 @@
|
||||
/*!
|
||||
* HotPocket by BTHLabs (https://hotpocket.app/)
|
||||
* Copyright 2025-present BTHLabs <contact@bthlabs.pl> (https://bthlabs.pl/)
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
((HotPocket) => {
|
||||
class HotPocketUIPasteboardLinkPlugin {
|
||||
constructor (app) {
|
||||
}
|
||||
onLoad (event) {
|
||||
let canCopy = false;
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
canCopy = true;
|
||||
}
|
||||
|
||||
for (let pasteboardLink of document.querySelectorAll('.ui-pasteboard-link')) {
|
||||
if (canCopy === false) {
|
||||
pasteboardLink.classList.add('d-none');
|
||||
} else {
|
||||
pasteboardLink.addEventListener('click', this.onClick);
|
||||
}
|
||||
}
|
||||
}
|
||||
onClick = (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
const icon = event.target.querySelector('i.bi');
|
||||
icon.classList.replace('bi-clipboard-fill', 'bi-clipboard');
|
||||
|
||||
navigator.clipboard.writeText(event.target.href).
|
||||
then(() => {
|
||||
icon.classList.replace('bi-clipboard', 'bi-clipboard-fill');
|
||||
}).
|
||||
catch((reason) => {
|
||||
console.error('HotPocket.UI.PasteboardLink.onClick()', reason);
|
||||
window.alert('Could not copy the link :(');
|
||||
});
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
HotPocket.addPlugin('UI.PasteboardLink', (app) => {
|
||||
return new HotPocketUIPasteboardLinkPlugin(app);
|
||||
});
|
||||
})(window.HotPocket);
|
||||
@@ -41,7 +41,20 @@
|
||||
if (navigator.share) {
|
||||
shareButton.addEventListener('click', this.onShareButtonClick);
|
||||
} else {
|
||||
shareButton.addClass('d-none');
|
||||
shareButton.remove();
|
||||
}
|
||||
}
|
||||
|
||||
const uiPlugin = this.app.plugin('UI');
|
||||
for (let controlButton of document.querySelectorAll('#ViewAssociationView .ui-controls > a.btn')) {
|
||||
if (uiPlugin.jsEnabled === true) {
|
||||
if (controlButton.classList.contains('ui-noscript-show')) {
|
||||
controlButton.remove();
|
||||
}
|
||||
} else {
|
||||
if (controlButton.classList.contains('ui-noscript-hide')) {
|
||||
controlButton.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -59,12 +72,9 @@
|
||||
onShareButtonClick = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const shareUrl = new URL(window.location.href);
|
||||
shareUrl.searchParams.set('share', 'true');
|
||||
|
||||
navigator.share({
|
||||
title: document.title,
|
||||
url: shareUrl.toString(),
|
||||
url: event.target.href,
|
||||
});
|
||||
|
||||
return false;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
class HotPocketUIPlugin {
|
||||
constructor (app) {
|
||||
document.body.classList.add('ui-js-enabled');
|
||||
this.jsEnabled = document.body.classList.contains('ui-js-enabled');
|
||||
|
||||
if (window.navigator.standalone === true) {
|
||||
document.querySelector('body').classList.add('ui-mode-standalone');
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
</a>
|
||||
</p>
|
||||
{% if show_controls %}
|
||||
<p class="mb-3 text-center">
|
||||
{% spaceless %}
|
||||
<div class="d-flex justify-content-center mb-3">
|
||||
<div class="btn-group ui-controls" role="group">
|
||||
<a
|
||||
class="btn btn-primary btn-sm"
|
||||
href="{% url 'ui.associations.edit' pk=association.pk %}"
|
||||
@@ -35,14 +35,29 @@
|
||||
<i class="bi bi-pencil"></i> {% translate 'Edit' %}
|
||||
</a>
|
||||
<a
|
||||
class="btn btn-secondary btn-sm ms-2 ui-noscript-hide ui-share-button"
|
||||
href="#"
|
||||
class="btn btn-secondary btn-sm ui-noscript-hide ui-share-button"
|
||||
href="{{ share_url }}"
|
||||
role="button"
|
||||
>
|
||||
<i class="bi bi-box-arrow-up"></i> {% translate 'Share' %}
|
||||
</a>
|
||||
{% endspaceless %}
|
||||
</p>
|
||||
<a
|
||||
class="btn btn-secondary btn-sm ui-noscript-hide ui-pasteboard-link"
|
||||
href="{{ share_url }}"
|
||||
role="button"
|
||||
>
|
||||
<i class="bi bi-clipboard"></i> {% translate 'Copy share link' %}
|
||||
</a>
|
||||
<a
|
||||
class="btn btn-secondary btn-sm ui-noscript-show"
|
||||
href="{{ share_url }}"
|
||||
rel="noopener noreferer"
|
||||
target="_blank"
|
||||
>
|
||||
<i class="bi bi-link-45deg"></i> {% translate 'Share link' %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if association.description %}
|
||||
<div class="row mb-3">
|
||||
|
||||
@@ -40,7 +40,25 @@
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item ms-auto d-flex align-items-center">
|
||||
{% if not request.user.is_anonymous %}
|
||||
<li id="InlineCreateSaveForm" class="nav-item ms-auto me-1 d-flex align-items-center">
|
||||
<a class="nav-link px-1 py-0 fs-3 text-primary ui-inline-create-save-form-trigger" href="{% url 'ui.saves.create' %}">
|
||||
<i class="bi bi-plus-circle-fill"></i>
|
||||
</a>
|
||||
<form action="{% url 'ui.saves.create' %}" class="position-absolute d-none align-items-center bg-body-tertiary ui-inline-create-save-form" method="post">
|
||||
{% csrf_token %}
|
||||
<label class="visually-hidden" for="input-inline-create-save-form-url">{% translate 'URL' %}</label>
|
||||
<input class="form-control flex-grow-1 me-1 form-control-sm" id="input-inline-create-save-form-url" name="url" placeholder="{% translate 'URL' %}" type="text">
|
||||
<button class="btn btn-primary btn-sm flex-grow-0 flex-shrink-0 me-1" role="button">
|
||||
<i class="bi bi-floppy-fill"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-danger btn-sm flex-grow-0 flex-shrink-0 ui-inline-create-save-form-close">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item {% if request.user.is_anonymous %}ms-auto{% endif %} d-flex align-items-center">
|
||||
<a class="nav-link px-1 py-0 fs-3" data-bs-toggle="offcanvas" href="#offcanvas-controls" aria-controls="offcanvas-controls">
|
||||
<i class="bi bi-person-circle"></i>
|
||||
</a>
|
||||
@@ -74,20 +92,6 @@
|
||||
</template>
|
||||
</div>
|
||||
|
||||
{% if not request.user.is_anonymous %}
|
||||
<nav id="button-bar" class="navbar navbar-expand-sm bg-body-tertiary fixed-bottom {% block button_bar_class %}{% endblock %}">
|
||||
<div class="container">
|
||||
<ul class="navbar-nav my-0 mx-auto">
|
||||
<li class="nav-item d-flex align-items-center ms-2">
|
||||
<a class="nav-link py-1 px-1 text-primary ui-button-bar-link" href="{% url 'ui.saves.create' %}">
|
||||
<i class="bi bi-plus-circle-fill"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvas-controls" aria-labelledby="offcanvas-controls-label">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title" id="offcanvas-controls-label">
|
||||
@@ -147,6 +151,8 @@
|
||||
<script src="{% static 'ui/js/hotpocket-backend.ui.BrowseSavesView.js' %}" type="text/javascript"></script>
|
||||
<script src="{% static 'ui/js/hotpocket-backend.ui.ViewAssociationView.js' %}" type="text/javascript"></script>
|
||||
<script src="{% static 'ui/js/hotpocket-backend.ui.BrowseAccountAppsView.js' %}" type="text/javascript"></script>
|
||||
<script src="{% static 'ui/js/hotpocket-backend.ui.InlineCreateSaveForm.js' %}" type="text/javascript"></script>
|
||||
<script src="{% static 'ui/js/hotpocket-backend.ui.PasteboardLink.js' %}" type="text/javascript"></script>
|
||||
{% block page_scripts %}{% endblock %}
|
||||
<script type="text/javascript">
|
||||
(() => {
|
||||
|
||||
@@ -132,10 +132,15 @@ def render_access_token_app(access_token: AccessTokenOut) -> str:
|
||||
variant = 'secondary'
|
||||
|
||||
origin_app = access_token.get_origin_app()
|
||||
match origin_app:
|
||||
case AccessTokenOriginApp.SAFARI_WEB_EXTENSION:
|
||||
app = UIAccessTokenOriginApp[origin_app.value].value
|
||||
variant = 'info'
|
||||
|
||||
extension_origin_apps = (
|
||||
AccessTokenOriginApp.SAFARI_WEB_EXTENSION,
|
||||
AccessTokenOriginApp.CHROME_EXTENSION,
|
||||
AccessTokenOriginApp.FIREFOX_EXTENSION,
|
||||
)
|
||||
if origin_app in extension_origin_apps:
|
||||
app = UIAccessTokenOriginApp[origin_app.value].value
|
||||
variant = 'info'
|
||||
|
||||
return format_html(
|
||||
'<span class="badge text-bg-{}">{}</span>',
|
||||
|
||||
@@ -198,12 +198,21 @@ def view(request: HttpRequest, pk: uuid.UUID) -> HttpResponse:
|
||||
if is_share is True:
|
||||
show_controls = show_controls and False
|
||||
|
||||
share_url = reverse(
|
||||
'ui.associations.view',
|
||||
args=(association.pk,),
|
||||
query=[
|
||||
('share', 'true'),
|
||||
],
|
||||
)
|
||||
|
||||
return render(
|
||||
request,
|
||||
'ui/associations/view.html',
|
||||
{
|
||||
'association': association,
|
||||
'show_controls': show_controls,
|
||||
'share_url': share_url,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ def manifest_json(request: HttpRequest) -> JsonResponse:
|
||||
'type': 'image/png',
|
||||
},
|
||||
],
|
||||
'scope': '/',
|
||||
'share_target': {
|
||||
'action': request.build_absolute_uri(
|
||||
reverse('ui.integrations.android.share_sheet'),
|
||||
|
||||
@@ -72,6 +72,7 @@ JSONRPC_METHOD_MODULES = [
|
||||
|
||||
CORS_ALLOWED_ORIGIN_REGEXES = [
|
||||
r'safari-web-extension:\/\/.+?',
|
||||
r'chrome-extension:\/\/.+?',
|
||||
]
|
||||
CORS_ALLOW_CREDENTIALS = True
|
||||
CORS_ALLOW_HEADERS = (
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
run:
|
||||
echo: true
|
||||
pty: true
|
||||
files_to_version:
|
||||
- "hotpocket_backend/_meta.py"
|
||||
- "ops/bin/entrypoint-deployment.sh"
|
||||
- "package.json"
|
||||
- "pyproject.toml"
|
||||
|
||||
@@ -13,7 +13,7 @@ cat <<EOF
|
||||
|_|
|
||||
production
|
||||
|
||||
HotPocket v25.9.12 [${HOTPOCKET_BACKEND_IMAGE_ID}] (https://hotpocket.app/)
|
||||
HotPocket v25.9.18 [${HOTPOCKET_BACKEND_IMAGE_ID}] (https://hotpocket.app/)
|
||||
Copyright 2025-present by BTHLabs. All rights reserved. (https://bthlabs.pl/)
|
||||
Licensed under Apache-2.0
|
||||
EOF
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hotpocket-backend",
|
||||
"version": "25.9.12",
|
||||
"version": "25.9.18",
|
||||
"description": "HotPocket Backend",
|
||||
"main": "hotpocket_backend/apps/frontend/src/index.js",
|
||||
"repository": "https://git.bthlabs.pl/tomekwojcik/hotpocket",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "hotpocket-backend"
|
||||
version = "25.9.12"
|
||||
version = "25.9.18"
|
||||
description = "HotPocket Backend"
|
||||
authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -5,32 +5,33 @@ from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
from invoke import task
|
||||
from invoke import Context, task
|
||||
from invoke.exceptions import UnexpectedExit
|
||||
|
||||
from hotpocket_workspace_tools import WorkspaceMode, get_workspace_mode
|
||||
from hotpocket_workspace_tools.tasks import * # noqa: F401,F403
|
||||
|
||||
WORKSPACE_MODE = get_workspace_mode()
|
||||
ENV = os.getenv('HOTPOCKET_BACKEND_ENV', 'docker')
|
||||
|
||||
|
||||
@task
|
||||
def clean(ctx):
|
||||
def clean(ctx: Context):
|
||||
ctx.run('rm -rf hotpocket_backend/static/')
|
||||
|
||||
|
||||
@task
|
||||
def test(ctx):
|
||||
def test(ctx: Context):
|
||||
ctx.run('pytest -v --disable-warnings')
|
||||
|
||||
|
||||
@task
|
||||
def flake8(ctx):
|
||||
def flake8(ctx: Context):
|
||||
ctx.run('flake8')
|
||||
|
||||
|
||||
@task
|
||||
def isort(ctx, check=False, diff=False):
|
||||
def isort(ctx: Context, check=False, diff=False):
|
||||
command_parts = [
|
||||
'isort',
|
||||
]
|
||||
@@ -47,12 +48,12 @@ def isort(ctx, check=False, diff=False):
|
||||
|
||||
|
||||
@task
|
||||
def eslint(ctx):
|
||||
def eslint(ctx: Context):
|
||||
ctx.run('yarn run eslint')
|
||||
|
||||
|
||||
@task
|
||||
def lint(ctx):
|
||||
def lint(ctx: Context):
|
||||
ihazsuccess = True
|
||||
|
||||
try:
|
||||
@@ -75,17 +76,17 @@ def lint(ctx):
|
||||
|
||||
|
||||
@task
|
||||
def typecheck(ctx):
|
||||
def typecheck(ctx: Context):
|
||||
ctx.run('mypy .')
|
||||
|
||||
|
||||
@task
|
||||
def django_shell(ctx):
|
||||
def django_shell(ctx: Context):
|
||||
ctx.run('python manage.py shell_plus')
|
||||
|
||||
|
||||
@task
|
||||
def ci(ctx):
|
||||
def ci(ctx: Context):
|
||||
ihazsuccess = True
|
||||
|
||||
ci_tasks = [test, lint, typecheck]
|
||||
@@ -100,7 +101,7 @@ def ci(ctx):
|
||||
|
||||
|
||||
@task
|
||||
def setup(ctx):
|
||||
def setup(ctx: Context):
|
||||
ctx.run('python manage.py migrate')
|
||||
ctx.run('python manage.py create_initial_account hotpocket hotpocketm4st3r')
|
||||
|
||||
@@ -109,13 +110,13 @@ def setup(ctx):
|
||||
|
||||
|
||||
@task
|
||||
def start_web(ctx):
|
||||
def start_web(ctx: Context):
|
||||
bind = os.getenv('HOTPOCKET_BACKEND_RUNSERVER_BIND_WEB', '127.0.0.1:8001')
|
||||
ctx.run(f'python manage.py runserver {bind}')
|
||||
|
||||
|
||||
@task
|
||||
def start_admin(ctx):
|
||||
def start_admin(ctx: Context):
|
||||
bind = os.getenv('HOTPOCKET_BACKEND_RUNSERVER_BIND_ADMIN', '127.0.0.1:8002')
|
||||
ctx.run(f'python manage.py runserver {bind}', env=dict(
|
||||
DJANGO_SETTINGS_MODULE=f'hotpocket_backend.settings.{ENV}.admin',
|
||||
@@ -124,7 +125,7 @@ def start_admin(ctx):
|
||||
|
||||
|
||||
@task
|
||||
def start_celery_worker(ctx, concurrency=2):
|
||||
def start_celery_worker(ctx: Context, concurrency=2):
|
||||
command_parts = [
|
||||
'celery',
|
||||
'-A hotpocket_backend.celery:app',
|
||||
@@ -137,5 +138,5 @@ def start_celery_worker(ctx, concurrency=2):
|
||||
|
||||
|
||||
@task
|
||||
def start_celery_beat(ctx, schedule_path='run/celery-beat-schedule'):
|
||||
def start_celery_beat(ctx: Context, schedule_path='run/celery-beat-schedule'):
|
||||
ctx.run(f'celery -A hotpocket_backend.celery:app beat -l INFO -s {schedule_path}')
|
||||
|
||||
@@ -27,6 +27,14 @@ def test_authenticated_ok(authenticated_client: Client,
|
||||
assert hasattr(result.context['association'], 'target') is True
|
||||
assert result.context['association'].target.pk == association_out.target.pk
|
||||
assert result.context['show_controls'] is True
|
||||
assert 'share_url' in result.context
|
||||
|
||||
expected_share_url = reverse(
|
||||
'ui.associations.view',
|
||||
args=(association_out.pk,),
|
||||
query=[('share', 'true')],
|
||||
)
|
||||
assert result.context['share_url'] == expected_share_url
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
||||
2
services/extension/.gitignore
vendored
2
services/extension/.gitignore
vendored
@@ -1,2 +1,4 @@
|
||||
node_modules/
|
||||
dist/
|
||||
secrets/*.json
|
||||
secrets/*.pem
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"message": "Save to HotPocket",
|
||||
"description": "The display name for the extension."
|
||||
},
|
||||
"extension_name_development": {
|
||||
"message": "HotPocket Development",
|
||||
"description": "The display name for the extension."
|
||||
},
|
||||
"extension_description": {
|
||||
"message": "Save to HotPocket. Straight from the toolbar!",
|
||||
"description": "Description of what the extension does."
|
||||
|
||||
BIN
services/extension/assets/images/icon-16.png
Normal file
BIN
services/extension/assets/images/icon-16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 874 B |
BIN
services/extension/assets/images/icon-32.png
Normal file
BIN
services/extension/assets/images/icon-32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
BIN
services/extension/assets/images/toolbar-icon-16.png
Normal file
BIN
services/extension/assets/images/toolbar-icon-16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 594 B |
BIN
services/extension/assets/images/toolbar-icon-32.png
Normal file
BIN
services/extension/assets/images/toolbar-icon-32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -5,9 +5,9 @@ import globals from 'globals';
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
files: [
|
||||
'eslint.config.js',
|
||||
'src/**/*.js',
|
||||
ignores: [
|
||||
'dist/**',
|
||||
'rollup.config.js',
|
||||
],
|
||||
plugins: {
|
||||
js,
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
run:
|
||||
echo: true
|
||||
pty: true
|
||||
files_to_version:
|
||||
- "src/content/preauth.html"
|
||||
- "src/manifest/common.json"
|
||||
- "package.json"
|
||||
- "pyproject.toml"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hotpocket-extension",
|
||||
"version": "25.9.12",
|
||||
"version": "25.9.17",
|
||||
"description": "HotPocket Extension",
|
||||
"main": "src/index.js",
|
||||
"repository": "https://git.bthlabs.pl/tomekwojcik/hotpocket",
|
||||
@@ -12,7 +12,13 @@
|
||||
"eslint": "npx eslint .",
|
||||
"build:safari": "NODE_ENV=production HOTPOCKET_EXTENSION_TARGET=safari npx rollup -c rollup.config.js",
|
||||
"dev:safari": "HOTPOCKET_EXTENSION_TARGET=safari npx rollup -c rollup.config.js",
|
||||
"watch:safari": "HOTPOCKET_EXTENSION_TARGET=safari npx rollup -c rollup.config.js -w"
|
||||
"watch:safari": "HOTPOCKET_EXTENSION_TARGET=safari npx rollup -c rollup.config.js -w",
|
||||
"build:chrome": "NODE_ENV=production HOTPOCKET_EXTENSION_TARGET=chrome npx rollup -c rollup.config.js",
|
||||
"dev:chrome": "HOTPOCKET_EXTENSION_TARGET=chrome npx rollup -c rollup.config.js",
|
||||
"watch:chrome": "HOTPOCKET_EXTENSION_TARGET=chrome npx rollup -c rollup.config.js -w",
|
||||
"build:firefox": "NODE_ENV=production HOTPOCKET_EXTENSION_TARGET=firefox npx rollup -c rollup.config.js",
|
||||
"dev:firefox": "HOTPOCKET_EXTENSION_TARGET=firefox npx rollup -c rollup.config.js",
|
||||
"watch:firefox": "HOTPOCKET_EXTENSION_TARGET=firefox npx rollup -c rollup.config.js -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "9.33.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "hotpocket-extension"
|
||||
version = "25.9.12"
|
||||
version = "25.9.17"
|
||||
description = "HotPocket Extension"
|
||||
authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -3,7 +3,9 @@ import copy from 'rollup-plugin-copy';
|
||||
import {string} from 'rollup-plugin-string';
|
||||
|
||||
import packageJSON from './package.json' with {type: 'json'};
|
||||
import manifestChrome from './src/manifest/chrome.json' with {type: 'json'};
|
||||
import manifestCommon from './src/manifest/common.json' with {type: 'json'};
|
||||
import manifestFirefox from './src/manifest/firefox.json' with {type: 'json'};
|
||||
import manifestSafari from './src/manifest/safari.json' with {type: 'json'};
|
||||
|
||||
const BANNER = `/*!
|
||||
@@ -38,7 +40,9 @@ if (BASE_URL === null) {
|
||||
|
||||
const PLUGINS = [
|
||||
string({
|
||||
include: /\.html$/,
|
||||
include: [
|
||||
'src/content/templates/*.html',
|
||||
],
|
||||
}),
|
||||
replace({
|
||||
include: /\.js$/,
|
||||
@@ -62,12 +66,22 @@ const manifestJsonOutputPlugin = () => {
|
||||
...result,
|
||||
...manifestSafari,
|
||||
};
|
||||
} else if (TARGET == 'chrome') {
|
||||
result = {
|
||||
...result,
|
||||
...manifestChrome,
|
||||
};
|
||||
} else if (TARGET == 'firefox') {
|
||||
result = {
|
||||
...result,
|
||||
...manifestFirefox,
|
||||
};
|
||||
}
|
||||
|
||||
result.version = packageJSON.version;
|
||||
|
||||
if (IS_PRODUCTION === false) {
|
||||
result.name = 'HotPocket Development';
|
||||
result.name = '__MSG_extension_name_development__';
|
||||
}
|
||||
|
||||
return JSON.stringify(result, null, 2);
|
||||
@@ -78,6 +92,8 @@ const manifestJsonOutputPlugin = () => {
|
||||
let OUTPUT_PATH = `dist/${TARGET}`;
|
||||
if (TARGET === 'safari') {
|
||||
OUTPUT_PATH = '../apple/Shared (Extension)/Resources';
|
||||
} else if (IS_PRODUCTION === true) {
|
||||
OUTPUT_PATH = `dist/${TARGET}-production`;
|
||||
}
|
||||
|
||||
export default [
|
||||
@@ -101,6 +117,13 @@ export default [
|
||||
src: 'assets/images',
|
||||
dest: OUTPUT_PATH,
|
||||
},
|
||||
{
|
||||
src: [
|
||||
'src/content/preauth.html',
|
||||
'src/content/preauth.js',
|
||||
],
|
||||
dest: OUTPUT_PATH,
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
|
||||
0
services/extension/secrets/.placeholder
Normal file
0
services/extension/secrets/.placeholder
Normal file
6
services/extension/src/background/chrome.js
Normal file
6
services/extension/src/background/chrome.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import main from './main';
|
||||
|
||||
main({
|
||||
platform: 'Chrome',
|
||||
api: chrome,
|
||||
});
|
||||
6
services/extension/src/background/firefox.js
Normal file
6
services/extension/src/background/firefox.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import main from './main';
|
||||
|
||||
main({
|
||||
platform: 'Firefox',
|
||||
api: browser,
|
||||
});
|
||||
@@ -1,8 +1,23 @@
|
||||
import HotPocketExtension from '../common';
|
||||
|
||||
const AUTH_URL = (new URL('/integrations/extension/authenticate/', HotPocketExtension.base_url)).toString();
|
||||
const POST_AUTH_URL = (new URL('/integrations/extension/post-authenticate/', HotPocketExtension.base_url)).toString();
|
||||
const RPC_URL = (new URL('/rpc/', HotPocketExtension.base_url)).toString();
|
||||
const POST_AUTH_PATH = '/integrations/extension/post-authenticate/';
|
||||
const RPC_PATH = '/rpc/';
|
||||
|
||||
let authSessionToken = null;
|
||||
let rpcURL = null;
|
||||
|
||||
const updateRpcURL = () => {
|
||||
rpcURL = null;
|
||||
if (HotPocketExtension.base_url !== null) {
|
||||
rpcURL = (new URL(RPC_PATH, HotPocketExtension.base_url)).toString();
|
||||
}
|
||||
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.background.updateRpcURL()',
|
||||
HotPocketExtension.base_url,
|
||||
rpcURL,
|
||||
);
|
||||
};
|
||||
|
||||
const makeJSONRPCCall = (method, params) => {
|
||||
return {
|
||||
@@ -43,7 +58,7 @@ const executeJSONRPCCall = async (url, call, {accessToken}) => {
|
||||
},
|
||||
);
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.content.executeJSONRPCCall()', response,
|
||||
'HotPocketExtension.background.executeJSONRPCCall()', response,
|
||||
);
|
||||
|
||||
if (response.status !== 200) {
|
||||
@@ -56,7 +71,7 @@ const executeJSONRPCCall = async (url, call, {accessToken}) => {
|
||||
const callResult = await response.json();
|
||||
if (callResult.error) {
|
||||
HotPocketExtension.LOGGER.error(
|
||||
'HotPocketExtension.content.executeJSONRPCCall(): RPC error',
|
||||
'HotPocketExtension.background.executeJSONRPCCall(): RPC error',
|
||||
callResult.error.code,
|
||||
callResult.error.message,
|
||||
callResult.error.data,
|
||||
@@ -69,7 +84,7 @@ const executeJSONRPCCall = async (url, call, {accessToken}) => {
|
||||
}
|
||||
} catch (exception) {
|
||||
HotPocketExtension.LOGGER.error(
|
||||
'HotPocketExtension.content.executeJSONRPCCall(): Fetch error', exception,
|
||||
'HotPocketExtension.background.executeJSONRPCCall(): Fetch error', exception,
|
||||
);
|
||||
error = {
|
||||
code: -32000,
|
||||
@@ -90,9 +105,9 @@ const getAccessTokenMeta = () => {
|
||||
|
||||
const doSave = async (accessToken, tab) => {
|
||||
const call = makeJSONRPCCall('saves.create', [tab.url]);
|
||||
const [result, error] = await executeJSONRPCCall(RPC_URL, call, {accessToken});
|
||||
const [result, error] = await executeJSONRPCCall(rpcURL, call, {accessToken});
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.content.doSave():', result, error,
|
||||
'HotPocketExtension.background.doSave():', result, error,
|
||||
);
|
||||
|
||||
if (error !== null) {
|
||||
@@ -112,7 +127,7 @@ const doCreateAndStoreAccessToken = async (authKey) => {
|
||||
);
|
||||
|
||||
const [accessToken, error] = await executeJSONRPCCall(
|
||||
RPC_URL, accessTokenCall, {accessToken: null},
|
||||
rpcURL, accessTokenCall, {accessToken: null},
|
||||
);
|
||||
|
||||
if (error === null) {
|
||||
@@ -125,15 +140,34 @@ const doCreateAndStoreAccessToken = async (authKey) => {
|
||||
};
|
||||
|
||||
const doHandleAuthFlow = (authTab) => {
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.background.doHandleAuthFlow()', authTab,
|
||||
);
|
||||
let currentAuthTabId = authTab.id;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const onTabsUpdated = (tabId, changeInfo, updatedTab) => {
|
||||
if (tabId === authTab.id) {
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.content.doHandleAuthFlow.onTabsUpdated()', updatedTab, changeInfo,
|
||||
);
|
||||
const changedURL = changeInfo.url;
|
||||
|
||||
const changedURL = changeInfo.url;
|
||||
if (changedURL && changedURL.startsWith(POST_AUTH_URL)) {
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.background.doHandleAuthFlow.onTabsUpdated()',
|
||||
updatedTab,
|
||||
changedURL,
|
||||
(changedURL && changedURL.includes(POST_AUTH_PATH)),
|
||||
changeInfo,
|
||||
);
|
||||
|
||||
const expectedSessionTabQuery = `?authSessionToken=${authSessionToken}`;
|
||||
if (tabId !== currentAuthTabId && changedURL.includes(expectedSessionTabQuery)) {
|
||||
// When redirecting from the preauth page to the HotPocket instance,
|
||||
// Safari "replaces" the auth tab with a new one. This nasty hack will
|
||||
// allow the extension to keep track of it.
|
||||
// I hate computers.
|
||||
currentAuthTabId = tabId;
|
||||
}
|
||||
|
||||
if (tabId === currentAuthTabId) {
|
||||
if (changedURL && changedURL.includes(POST_AUTH_PATH)) {
|
||||
const parsedChangedURL = new URL(changedURL);
|
||||
const authKey = parsedChangedURL.searchParams.get('auth_key');
|
||||
|
||||
@@ -154,8 +188,9 @@ const doHandleAuthFlow = (authTab) => {
|
||||
reject(error);
|
||||
}).
|
||||
finally(() => {
|
||||
authSessionToken = null;
|
||||
HotPocketExtension.api.tabs.onUpdated.removeListener(onTabsUpdated);
|
||||
HotPocketExtension.api.tabs.remove(authTab.id);
|
||||
HotPocketExtension.api.tabs.remove(currentAuthTabId);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -175,7 +210,7 @@ const doCheckAuth = async (accessToken) => {
|
||||
[accessToken, getAccessTokenMeta()],
|
||||
);
|
||||
|
||||
const [result, error] = await executeJSONRPCCall(RPC_URL, call, {
|
||||
const [result, error] = await executeJSONRPCCall(rpcURL, call, {
|
||||
accessToken,
|
||||
});
|
||||
|
||||
@@ -196,15 +231,28 @@ const doCheckAuth = async (accessToken) => {
|
||||
return accessToken;
|
||||
};
|
||||
|
||||
const doGetAccessToken = async () => {
|
||||
let storageResult = await HotPocketExtension.api.storage.local.get('accessToken');
|
||||
let accessToken = await doCheckAuth(
|
||||
storageResult.accessToken || null,
|
||||
const doSetupRPC = async () => {
|
||||
let storageResult = await HotPocketExtension.api.storage.local.get(
|
||||
['accessToken', 'baseURL'],
|
||||
);
|
||||
|
||||
let accessToken = null;
|
||||
if (storageResult.baseURL) {
|
||||
HotPocketExtension.base_url = storageResult.baseURL;
|
||||
updateRpcURL();
|
||||
|
||||
accessToken = await doCheckAuth(
|
||||
storageResult.accessToken || null,
|
||||
);
|
||||
}
|
||||
|
||||
if (accessToken === null) {
|
||||
authSessionToken = crypto.randomUUID();
|
||||
|
||||
const authTab = await HotPocketExtension.api.tabs.create({
|
||||
url: AUTH_URL,
|
||||
url: HotPocketExtension.api.runtime.getURL(
|
||||
`preauth.html?authSessionToken=${authSessionToken}`,
|
||||
),
|
||||
});
|
||||
|
||||
accessToken = await doHandleAuthFlow(authTab);
|
||||
@@ -217,24 +265,53 @@ const doSendTabMessage = (tab, message) => {
|
||||
HotPocketExtension.api.tabs.sendMessage(tab.id, message).
|
||||
then((result) => {
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.content.doSendTabMessage(): message sent', message, result,
|
||||
'HotPocketExtension.background.doSendTabMessage(): message sent',
|
||||
message,
|
||||
result,
|
||||
);
|
||||
}).
|
||||
catch((error) => {
|
||||
HotPocketExtension.LOGGER.error(
|
||||
'HotPocketExtension.content.doSendTabMessage(): could not send message', error,
|
||||
'HotPocketExtension.background.doSendTabMessage(): could not send message',
|
||||
error,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const doUpdateBaseURL = (nextBaseURL) => {
|
||||
HotPocketExtension.base_url = nextBaseURL;
|
||||
updateRpcURL();
|
||||
|
||||
HotPocketExtension.api.storage.local.
|
||||
set({
|
||||
baseURL: nextBaseURL,
|
||||
}).
|
||||
then(() => {
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.background.doUpdateBaseURL()', 'Base URL saved',
|
||||
);
|
||||
}).
|
||||
catch((error) => {
|
||||
HotPocketExtension.LOGGER.error(
|
||||
'HotPocketExtension.background.doUpdateBaseURL()', error,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const onTabCreated = (tab) => {
|
||||
HotPocketExtension.LOGGER.debug('HotPocketExtension.onTabCreated()', tab);
|
||||
HotPocketExtension.api.action.enable(tab.id);
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.background.onTabCreated()', tab,
|
||||
);
|
||||
HotPocketExtension.api.action.enable(tab.id).catch((error) => {
|
||||
HotPocketExtension.LOGGER.error(
|
||||
'HotPocketExtension.background.onTabCreated()', tab.id, error,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const onBrowserActionClicked = async (tab) => {
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.onBrowserActionClicked()', tab.url,
|
||||
'HotPocketExtension.background.onBrowserActionClicked()', tab.url,
|
||||
);
|
||||
|
||||
if (!tab.url) {
|
||||
@@ -245,11 +322,11 @@ const onBrowserActionClicked = async (tab) => {
|
||||
let error = null;
|
||||
|
||||
try {
|
||||
let accessToken = await doGetAccessToken();
|
||||
let accessToken = await doSetupRPC();
|
||||
|
||||
result = await doSave(accessToken, tab);
|
||||
HotPocketExtension.LOGGER.debug(
|
||||
'HotPocketExtension.onBrowserActionClicked()', result,
|
||||
'HotPocketExtension.background.onBrowserActionClicked()', result,
|
||||
);
|
||||
} catch (exception) {
|
||||
HotPocketExtension.LOGGER.error(
|
||||
@@ -276,10 +353,11 @@ const onMessage = (message, sender, sendResponse) => {
|
||||
let response = {ok: true};
|
||||
try {
|
||||
if (message.type === 'HotPocket:Extension:ping') {
|
||||
HotPocketExtension.LOGGER.debug(sender.tab.id);
|
||||
doSendTabMessage(sender.tab, {
|
||||
type: 'HotPocket:Extension:pong',
|
||||
});
|
||||
} else if (message.type === 'HotPocket:Extension:setBaseURL') {
|
||||
doUpdateBaseURL(message.result);
|
||||
}
|
||||
} catch (exception) {
|
||||
HotPocketExtension.LOGGER.error(
|
||||
@@ -300,6 +378,8 @@ export default ({...configuration}) => {
|
||||
background: true,
|
||||
});
|
||||
|
||||
updateRpcURL();
|
||||
|
||||
HotPocketExtension.api.tabs.onCreated.addListener(onTabCreated);
|
||||
|
||||
HotPocketExtension.api.action.onClicked.addListener(onBrowserActionClicked);
|
||||
|
||||
@@ -8,7 +8,7 @@ const HotPocketExtension = {
|
||||
version: __HOTPOCKET_EXTENSION_VERSION__,
|
||||
debug: DEBUG,
|
||||
api: null,
|
||||
base_url: __HOTPOCKET_EXTENSION_BASE_URL__,
|
||||
base_url: null,
|
||||
LOGGER: {
|
||||
// eslint-disable-next-line no-console
|
||||
debug: (DEBUG === true) ? console.log : noop,
|
||||
|
||||
6
services/extension/src/content/chrome.js
Normal file
6
services/extension/src/content/chrome.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import main from './main';
|
||||
|
||||
main({
|
||||
platform: 'Chrome',
|
||||
api: window.chrome,
|
||||
});
|
||||
6
services/extension/src/content/firefox.js
Normal file
6
services/extension/src/content/firefox.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import main from './main';
|
||||
|
||||
main({
|
||||
platform: 'Firefox',
|
||||
api: browser,
|
||||
});
|
||||
91
services/extension/src/content/preauth.html
Normal file
91
services/extension/src/content/preauth.html
Normal file
File diff suppressed because one or more lines are too long
45
services/extension/src/content/preauth.js
Normal file
45
services/extension/src/content/preauth.js
Normal file
@@ -0,0 +1,45 @@
|
||||
/*!
|
||||
* HotPocket by BTHLabs (https://hotpocket.app/)
|
||||
* Copyright 2025-present BTHLabs <contact@bthlabs.pl> (https://bthlabs.pl/)
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
(() => {
|
||||
'use strict';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', (event) => {
|
||||
const form = document.getElementById('PreauthForm');
|
||||
form.addEventListener('submit', (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
const inputBaseURL = document.getElementById('id_base_url');
|
||||
const baseURL = inputBaseURL.value;
|
||||
|
||||
let api = window.browser || null;
|
||||
if (api === null && window.chrome) {
|
||||
api = window.chrome;
|
||||
}
|
||||
|
||||
api.runtime.sendMessage({
|
||||
type: 'HotPocket:Extension:setBaseURL',
|
||||
result: baseURL,
|
||||
});
|
||||
|
||||
const loginURL = new URL('/integrations/extension/authenticate/', inputBaseURL.value);
|
||||
window.location.replace(loginURL.toString());
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
})();
|
||||
13
services/extension/src/manifest/chrome.json
Normal file
13
services/extension/src/manifest/chrome.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"action": {
|
||||
"default_title": "__MSG_extension_name__",
|
||||
"default_icon": {
|
||||
"16": "images/toolbar-icon-16.png",
|
||||
"32": "images/toolbar-icon-32.png"
|
||||
}
|
||||
},
|
||||
"background": {
|
||||
"service_worker": "background-bundle.js",
|
||||
"type": "module"
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,10 @@
|
||||
"default_locale": "en",
|
||||
"name": "__MSG_extension_name__",
|
||||
"description": "__MSG_extension_description__",
|
||||
"version": "25.9.12",
|
||||
"version": "25.9.17",
|
||||
"icons": {
|
||||
"16": "images/icon-16.png",
|
||||
"32": "images/icon-32.png",
|
||||
"48": "images/icon-48.png",
|
||||
"64": "images/icon-64.png",
|
||||
"96": "images/icon-96.png",
|
||||
@@ -22,10 +24,6 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"action": {
|
||||
"default_title": "__MSG_extension_name__",
|
||||
"default_icon": "images/toolbar-icon.svg"
|
||||
},
|
||||
"permissions": [
|
||||
"storage",
|
||||
"activeTab",
|
||||
|
||||
27
services/extension/src/manifest/firefox.json
Normal file
27
services/extension/src/manifest/firefox.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"action": {
|
||||
"default_title": "__MSG_extension_name__",
|
||||
"default_icon": {
|
||||
"16": "images/toolbar-icon-16.png",
|
||||
"32": "images/toolbar-icon-32.png"
|
||||
}
|
||||
},
|
||||
"background": {
|
||||
"scripts": [
|
||||
"background-bundle.js"
|
||||
],
|
||||
"type": "module"
|
||||
},
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "@Extension.HotPocket.BTHLabs",
|
||||
"strict_min_version": "142.0",
|
||||
"data_collection_permissions": {
|
||||
"required": [
|
||||
"websiteActivity",
|
||||
"browsingActivity"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"description": "__MSG_extension_description_Safari__",
|
||||
"action": {
|
||||
"default_title": "__MSG_extension_name__",
|
||||
"default_icon": "images/toolbar-icon.svg"
|
||||
},
|
||||
"background": {
|
||||
"scripts": [
|
||||
"background-bundle.js"
|
||||
|
||||
@@ -3,27 +3,30 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from invoke import task
|
||||
import json
|
||||
|
||||
from invoke import Context, task
|
||||
from invoke.exceptions import UnexpectedExit
|
||||
|
||||
from hotpocket_workspace_tools import get_workspace_mode
|
||||
from hotpocket_workspace_tools.tasks import * # noqa: F401,F403
|
||||
|
||||
WORKSPACE_MODE = get_workspace_mode()
|
||||
|
||||
|
||||
@task
|
||||
def clean(ctx):
|
||||
def clean(ctx: Context):
|
||||
ctx.run('rm -rf dist/')
|
||||
|
||||
|
||||
@task
|
||||
def test(ctx):
|
||||
def test(ctx: Context):
|
||||
# ctx.run('pytest -v --disable-warnings')
|
||||
print('NOOP')
|
||||
|
||||
|
||||
@task
|
||||
def flake8(ctx):
|
||||
def flake8(ctx: Context):
|
||||
ctx.run('flake8')
|
||||
|
||||
|
||||
@@ -45,12 +48,12 @@ def isort(ctx, check=False, diff=False):
|
||||
|
||||
|
||||
@task
|
||||
def eslint(ctx):
|
||||
def eslint(ctx: Context):
|
||||
ctx.run('yarn run eslint')
|
||||
|
||||
|
||||
@task
|
||||
def lint(ctx):
|
||||
def lint(ctx: Context):
|
||||
ihazsuccess = True
|
||||
|
||||
try:
|
||||
@@ -73,17 +76,17 @@ def lint(ctx):
|
||||
|
||||
|
||||
@task
|
||||
def typecheck(ctx):
|
||||
def typecheck(ctx: Context):
|
||||
ctx.run('mypy .')
|
||||
|
||||
|
||||
@task
|
||||
def django_shell(ctx):
|
||||
def django_shell(ctx: Context):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@task
|
||||
def ci(ctx):
|
||||
def ci(ctx: Context):
|
||||
ihazsuccess = True
|
||||
|
||||
ci_tasks = [test, lint, typecheck]
|
||||
@@ -98,15 +101,58 @@ def ci(ctx):
|
||||
|
||||
|
||||
@task
|
||||
def setup(ctx):
|
||||
def setup(ctx: Context):
|
||||
print('NOOP')
|
||||
|
||||
|
||||
@task
|
||||
def start_web(ctx):
|
||||
def start_web(ctx: Context):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@task
|
||||
def start_safari(ctx):
|
||||
def start_safari(ctx: Context):
|
||||
ctx.run('yarn watch:safari')
|
||||
|
||||
|
||||
@task(pre=[clean])
|
||||
def start_chrome(ctx: Context):
|
||||
ctx.run('yarn watch:chrome')
|
||||
|
||||
|
||||
@task(pre=[clean])
|
||||
def start_firefox(ctx: Context):
|
||||
ctx.run('yarn watch:firefox')
|
||||
|
||||
|
||||
@task
|
||||
def build_safari(ctx: Context):
|
||||
ctx.run('yarn build:safari')
|
||||
|
||||
|
||||
@task(pre=[clean])
|
||||
def build_chrome(ctx: Context):
|
||||
ctx.run('yarn build:chrome')
|
||||
ctx.run(' '.join([
|
||||
r'/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome',
|
||||
'--pack-extension=dist/chrome-production/',
|
||||
'--pack-extension-key=secrets/chrome.pem',
|
||||
]))
|
||||
|
||||
|
||||
@task(pre=[clean])
|
||||
def build_firefox(ctx: Context):
|
||||
ctx.run('yarn build:firefox')
|
||||
|
||||
firefox_secrets = None
|
||||
with open('secrets/firefox.json', 'r', encoding='utf-8') as firefox_secrets_f:
|
||||
firefox_secrets = json.load(firefox_secrets_f)
|
||||
|
||||
with ctx.cd('dist/firefox-production'):
|
||||
ctx.run(' '.join([
|
||||
'web-ext',
|
||||
'sign',
|
||||
'--channel=unlisted',
|
||||
f'--api-key={firefox_secrets["api_key"]}',
|
||||
f'--api-secret={firefox_secrets["api_secret"]}',
|
||||
]))
|
||||
|
||||
@@ -7,3 +7,5 @@ import enum
|
||||
class AccessTokenOriginApp(enum.Enum):
|
||||
UNKNOWN = 'UNKNOWN'
|
||||
SAFARI_WEB_EXTENSION = 'SAFARI_WEB_EXTENSION'
|
||||
CHROME_EXTENSION = 'CHROME_EXTENSION'
|
||||
FIREFOX_EXTENSION = 'FIREFOX_EXTENSION'
|
||||
|
||||
3
services/packages/poetry.lock
generated
3
services/packages/poetry.lock
generated
@@ -230,6 +230,9 @@ python-versions = "^3.12"
|
||||
files = []
|
||||
develop = true
|
||||
|
||||
[package.dependencies]
|
||||
invoke = "2.2.0"
|
||||
|
||||
[package.source]
|
||||
type = "directory"
|
||||
url = "workspace_tools"
|
||||
|
||||
@@ -26,6 +26,12 @@ class AccessTokenOut(ModelOut):
|
||||
case 'safari-web-extension':
|
||||
return AccessTokenOriginApp.SAFARI_WEB_EXTENSION
|
||||
|
||||
case 'chrome-extension':
|
||||
return AccessTokenOriginApp.CHROME_EXTENSION
|
||||
|
||||
case 'moz-extension':
|
||||
return AccessTokenOriginApp.FIREFOX_EXTENSION
|
||||
|
||||
case _:
|
||||
return None
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from .utils import bump_version # noqa: F401
|
||||
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
from invoke import Context, task
|
||||
|
||||
|
||||
@task
|
||||
def bump_version(ctx: Context, next_version: str, build: str | None = None):
|
||||
current_version_result = ctx.run('poetry version -s --no-ansi', hide='out')
|
||||
assert current_version_result is not None, 'Hm?'
|
||||
|
||||
current_version = current_version_result.stdout.strip()
|
||||
|
||||
print(f'Bumping version: `{current_version}` -> `{next_version}`')
|
||||
|
||||
for file_to_version in ctx.config.get('files_to_version', []):
|
||||
with open(file_to_version, 'r', encoding='utf-8') as content_f:
|
||||
content = content_f.read()
|
||||
|
||||
content = content.replace(current_version, next_version)
|
||||
|
||||
with open(file_to_version, 'w', encoding='utf-8') as content_f:
|
||||
content_f.write(content)
|
||||
@@ -8,6 +8,7 @@ readme = "README.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.12"
|
||||
invoke = "2.2.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
|
||||
77
tasks.py
77
tasks.py
@@ -1,11 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import datetime
|
||||
|
||||
from invoke import task
|
||||
from invoke import Context, task
|
||||
|
||||
from hotpocket_workspace_tools import get_workspace_mode, WorkspaceMode
|
||||
from hotpocket_workspace_tools.tasks import (
|
||||
bump_version as tools_bump_version_task,
|
||||
)
|
||||
|
||||
|
||||
class DockerBuildContext:
|
||||
@@ -60,10 +64,11 @@ class DockerBuildContext:
|
||||
|
||||
WORKSPACE_MODE = get_workspace_mode()
|
||||
|
||||
ALL_SERVICES = ['backend', 'packages', 'extension']
|
||||
ALL_SERVICES = ['apple', 'backend', 'extension', 'packages']
|
||||
VERSIONED_SERVICES = ['apple', 'backend', 'extension']
|
||||
|
||||
|
||||
def _run_in_service(ctx, service, command, **kwargs):
|
||||
def _run_in_service(ctx: Context, service, command, **kwargs):
|
||||
match WORKSPACE_MODE:
|
||||
case WorkspaceMode.DOCKER:
|
||||
return ctx.run(
|
||||
@@ -75,7 +80,7 @@ def _run_in_service(ctx, service, command, **kwargs):
|
||||
ctx.run(f'direnv exec . {command}')
|
||||
|
||||
|
||||
def _get_version(ctx, service):
|
||||
def _get_version(ctx: Context, service):
|
||||
with ctx.cd(f'services/{service}'):
|
||||
run_result = ctx.run('poetry version -s', hide='out')
|
||||
|
||||
@@ -88,27 +93,27 @@ def _get_head_sha(ctx):
|
||||
|
||||
|
||||
@task
|
||||
def clean(ctx, service):
|
||||
def clean(ctx: Context, service):
|
||||
_run_in_service(ctx, service, 'inv clean')
|
||||
|
||||
|
||||
@task
|
||||
def test(ctx, service):
|
||||
def test(ctx: Context, service):
|
||||
_run_in_service(ctx, service, 'inv test')
|
||||
|
||||
|
||||
@task
|
||||
def lint(ctx, service):
|
||||
def lint(ctx: Context, service):
|
||||
_run_in_service(ctx, service, 'inv lint')
|
||||
|
||||
|
||||
@task
|
||||
def typecheck(ctx, service):
|
||||
def typecheck(ctx: Context, service):
|
||||
_run_in_service(ctx, service, 'inv typecheck')
|
||||
|
||||
|
||||
@task
|
||||
def shell(ctx, service):
|
||||
def shell(ctx: Context, service):
|
||||
assert WORKSPACE_MODE == WorkspaceMode.DOCKER, (
|
||||
'Just `cd services/{service}` ;)'
|
||||
)
|
||||
@@ -116,12 +121,21 @@ def shell(ctx, service):
|
||||
|
||||
|
||||
@task
|
||||
def django_shell(ctx, service):
|
||||
def django_shell(ctx: Context, service):
|
||||
_run_in_service(ctx, service, 'inv django-shell')
|
||||
|
||||
|
||||
@task
|
||||
def build(ctx,
|
||||
def png_to_data_url(ctx: Context, png_path):
|
||||
with open(png_path, 'rb') as png_f:
|
||||
data = png_f.read()
|
||||
|
||||
encoded_data = base64.b64encode(data)
|
||||
print(f'data:image/png;base64,{encoded_data.decode("utf-8")}')
|
||||
|
||||
|
||||
@task
|
||||
def build(ctx: Context,
|
||||
service,
|
||||
context=None,
|
||||
builder=None,
|
||||
@@ -175,7 +189,7 @@ def build(ctx,
|
||||
|
||||
|
||||
@task
|
||||
def publish(ctx,
|
||||
def publish(ctx: Context,
|
||||
service,
|
||||
context=None,
|
||||
target='deployment',
|
||||
@@ -199,12 +213,12 @@ def publish(ctx,
|
||||
|
||||
|
||||
@task
|
||||
def ci(ctx, service):
|
||||
def ci(ctx: Context, service):
|
||||
_run_in_service(ctx, service, 'inv ci')
|
||||
|
||||
|
||||
@task
|
||||
def setup(ctx, service=None):
|
||||
def setup(ctx: Context, service=None):
|
||||
services_to_setup = []
|
||||
|
||||
services_to_setup = [*ALL_SERVICES]
|
||||
@@ -216,7 +230,7 @@ def setup(ctx, service=None):
|
||||
|
||||
|
||||
@task
|
||||
def install(ctx, service=None):
|
||||
def install(ctx: Context, service=None):
|
||||
services_to_setup = []
|
||||
|
||||
services_to_setup = [*ALL_SERVICES]
|
||||
@@ -228,7 +242,7 @@ def install(ctx, service=None):
|
||||
|
||||
|
||||
@task
|
||||
def lock(ctx, service):
|
||||
def lock(ctx: Context, service):
|
||||
_run_in_service(ctx, service, 'poetry lock --no-update')
|
||||
|
||||
|
||||
@@ -238,20 +252,43 @@ def start_cloud(ctx):
|
||||
|
||||
|
||||
@task
|
||||
def start_web(ctx, service):
|
||||
def start_web(ctx: Context, service):
|
||||
_run_in_service(ctx, service, 'inv start-web')
|
||||
|
||||
|
||||
@task
|
||||
def start_celery_worker(ctx, service):
|
||||
def start_celery_worker(ctx: Context, service):
|
||||
_run_in_service(ctx, service, 'inv start-worker')
|
||||
|
||||
|
||||
@task
|
||||
def start_celery_beat(ctx, service):
|
||||
def start_celery_beat(ctx: Context, service):
|
||||
_run_in_service(ctx, service, 'inv start-beat')
|
||||
|
||||
|
||||
@task
|
||||
def start_app(ctx, service, app):
|
||||
def start_app(ctx: Context, service, app):
|
||||
_run_in_service(ctx, service, f'inv start-{app}')
|
||||
|
||||
|
||||
@task
|
||||
def bump_version(ctx: Context,
|
||||
next_version: str,
|
||||
build: str | None = None,
|
||||
service: str | None = None,
|
||||
):
|
||||
assert build is not None, '`--build` is required here'
|
||||
|
||||
services_to_bump = [*VERSIONED_SERVICES]
|
||||
if service is not None:
|
||||
services_to_bump = [service]
|
||||
|
||||
for service_to_setup in services_to_bump:
|
||||
_run_in_service(
|
||||
ctx,
|
||||
service_to_setup,
|
||||
f'inv bump-version {next_version} --build {build}',
|
||||
)
|
||||
|
||||
if 'backend' in services_to_bump:
|
||||
tools_bump_version_task(ctx, next_version, build=build)
|
||||
|
||||
Reference in New Issue
Block a user