Skip to content

Dev/2.1.2#56

Merged
ink-developer merged 6 commits into
mainfrom
dev/2.1.2
Jun 3, 2026
Merged

Dev/2.1.2#56
ink-developer merged 6 commits into
mainfrom
dev/2.1.2

Conversation

@ink-developer
Copy link
Copy Markdown
Collaborator

@ink-developer ink-developer commented Jun 3, 2026

Описание

Кратко, что делает этот PR. Например, добавляет новый метод, исправляет баг, улучшает документацию.

Подготавливает релиз 2.1.2: исправляет обработку timeout и login-ответов без нового токена, убирает проблемный iterator у FolderList, обновляет publish workflow, bump версии и release notes.

Тип изменений

  • Исправление бага
  • Новая функциональность
  • Улучшение документации
  • Рефакторинг

Связанные задачи / Issue

Ссылка на issue, если есть: #
#55

Summary by CodeRabbit

  • New Features

    • Bot initialization (get_bot_init_data()) now works without specifying a chat ID for web-app scenarios.
  • Bug Fixes

    • Restored proper use of configured request timeout defaults unless explicitly overridden.
    • Fixed login responses to prevent token overwriting or client startup interruption.
    • Fixed folder list iteration behavior and serialization compatibility.
  • Chores

    • Version bumped to 2.1.2.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

PyMax 2.1.2 release updates the package version, makes chat_id optional for web app scenarios, fixes request timeout to default from config, makes login response token optional to prevent initialization failures, removes FolderList custom iteration for pydantic compatibility, and refactors the CI/CD publish workflow to use uv publish with artifact handoff.

Changes

PyMax 2.1.2 Release

Layer / File(s) Summary
Version 2.1.2 metadata
pyproject.toml, src/pymax/__init__.py, docs/index.rst
Version incremented from 2.1.1 to 2.1.2 across project metadata and release notes toctree entry.
Web app init data without chat_id
src/pymax/api/bots/payloads.py, src/pymax/api/bots/service.py, docs/release-2-1-2.rst
RequestInitDataPayload.chat_id and BotsService.get_init_data now accept optional chat_id for web app scenarios not tied to a specific chat context.
Request timeout defaulting to config value
src/pymax/app.py, tests/app/test_app_runtime.py
App.invoke default timeout changed from 30.0 to None; effective timeout computed from config.request_timeout when not explicitly provided, with per-invocation override support tested.
Login response and token handling
src/pymax/types/domain/login.py, src/pymax/app.py, docs/release-2-1-2.rst
LoginResponse.token now optional; App.start validates token is non-null before updating storage, improves device_id selection, and refines error messages for missing tokens and connection failures.
FolderList iteration model correction
src/pymax/types/domain/folder.py, tests/api/test_chat_user_self_session_services.py, docs/release-2-1-2.rst
FolderList.__iter__ method removed to prevent custom iteration override and pydantic compatibility issues; direct access via .folders attribute is now the standard pattern.
Release notes and documentation
docs/release-2-1-2.rst
Complete 2.1.2 release notes documenting feature additions, bug fixes, migration guidance, and workflow improvements.
CI/CD workflow refactoring
.github/workflows/publish.yml, docs/release-2-1-2.rst
Publish workflow refactored with concurrency configuration and simplified two-job structure: package job builds and validates distributions as artifacts; publish job downloads and releases to PyPI via uv publish instead of using pypa/gh-action-pypi-publish.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • MaxApiTeam/PyMax#53: Refactors the same .github/workflows/publish.yml workflow with overlapping changes to concurrency configuration and job structure (package/publish flow using uv).

Poem

🐰 Hops through the halls of version two-one-two,
Where web apps need no chats, and tokens start anew,
Timeouts heed the config's gentle call,
FolderLists skip dancing—pydantic needs them all!
And workflows now hop straight to PyPI's door.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Dev/2.1.2' is vague and lacks specificity about the actual changes; it only references the version number without describing what this release accomplishes. Use a more descriptive title that summarizes the main changes, such as 'Release 2.1.2: fix timeout handling, login token validation, and FolderList iterator'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description covers all required template sections with concrete details about the changes (timeout fixes, token handling, FolderList iterator removal, workflow updates) and includes a related issue reference.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev/2.1.2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
.github/workflows/publish.yml (1)

24-30: ⚡ Quick win

Pin the uv CLI version explicitly.

Both jobs install uv, but neither setup-uv step sets a version/version-file, and this repo’s pyproject.toml does not provide a required uv version. Per the action docs, that falls back to the latest uv release, so a future upstream uv change can alter or break the release pipeline without any change in this repo. (github.com)

Also applies to: 60-64

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/publish.yml around lines 24 - 30, The workflow's "Install
uv" steps use astral-sh/setup-uv without pinning a uv CLI version, so add an
explicit pin by updating the action inputs for the astral-sh/setup-uv steps (the
steps named "Install uv") to include either a fixed with: version:
"<UV_VERSION>" or point to a version-file (with: version-file:
"pyproject.toml"); do this for both occurrences of the Install uv step so the
release pipeline doesn't float to the latest upstream uv automatically.
src/pymax/api/bots/service.py (1)

23-30: ⚡ Quick win

Add a regression test for the omitted chat_id path.

This public API now supports get_init_data(bot_id, start_param=...), but the provided test coverage only asserts the serialized payload when chat_id is present. Please add a case that verifies chatId is omitted from the outgoing payload when chat_id=None, so this release behavior stays locked down.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pymax/api/bots/service.py` around lines 23 - 30, Add a regression test
that calls the public API method get_init_data(bot_id, start_param=...) (or via
the service wrapper that calls RequestInitDataPayload) with chat_id omitted and
asserts the outgoing payload passed to self.app.invoke
(Opcode.WEB_APP_INIT_DATA) does not include a chatId field; mock or spy on
service.app.invoke, capture its payload argument and verify
RequestInitDataPayload.to_payload() for the path where chat_id is None omits
chatId (use get_init_data and RequestInitDataPayload as the entrypoints to
locate code).
tests/app/test_app_runtime.py (1)

155-170: ⚡ Quick win

Add the matching regression test for tokenless login responses.

This file now covers the timeout fix, but the other runtime change in this PR—keeping the existing session token when LOGIN returns token=None—is still untested. A startup test that returns a login payload without a replacement token would lock in the new behavior and catch regressions in Lines 130-132 of src/pymax/app.py.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/app/test_app_runtime.py` around lines 155 - 170, Add a regression test
that verifies when the app receives a LOGIN response with token=None the
existing session token is preserved: create a RuntimeStore with
SessionInfo(token="token", ...), construct a RuntimeConnection whose startup
frames include a LOGIN response whose payload explicitly has token=None,
instantiate App (or call the startup method used in the runtime flow) with that
store and connection, run the startup/invoke sequence that processes LOGIN, then
assert the store.session.token remains "token" (unchanged) and that the
connection processed the LOGIN frame; reference runtime symbols RuntimeStore,
SessionInfo, RuntimeConnection, App, and the LOGIN response handling to locate
the code paths under test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/publish.yml:
- Around line 21-25: Replace all movable tag references in the workflow with
full commit SHAs: update uses: for actions/checkout@v6,
astral-sh/setup-uv@v8.1.0 (both occurrences), actions/upload-artifact@v4, and
actions/download-artifact@v4 to their corresponding full-length commit SHAs;
ensure the OIDC "publish" job's id-token: write step also points to the
SHA-pinned actions. Locate these by their uses: lines (e.g., "uses:
actions/checkout", "uses: astral-sh/setup-uv", "uses: actions/upload-artifact",
"uses: actions/download-artifact") and replace the tag suffix (e.g., `@v6`) with
the exact commit SHA obtained from each action's repo.

In `@src/pymax/app.py`:
- Line 36: The constructor currently treats any falsy config.store as missing by
using "self.config.store or SessionStore(...)", which overwrites intentionally
injected falsy stores; change the logic to an explicit None check so injected
stores are preserved: set self.store to config.store if config.store is not
None, otherwise construct a new SessionStore using SessionStore(config.work_dir,
config.session_name); update the assignment for self.store in the initializer
where self.config.store and SessionStore are referenced.

---

Nitpick comments:
In @.github/workflows/publish.yml:
- Around line 24-30: The workflow's "Install uv" steps use astral-sh/setup-uv
without pinning a uv CLI version, so add an explicit pin by updating the action
inputs for the astral-sh/setup-uv steps (the steps named "Install uv") to
include either a fixed with: version: "<UV_VERSION>" or point to a version-file
(with: version-file: "pyproject.toml"); do this for both occurrences of the
Install uv step so the release pipeline doesn't float to the latest upstream uv
automatically.

In `@src/pymax/api/bots/service.py`:
- Around line 23-30: Add a regression test that calls the public API method
get_init_data(bot_id, start_param=...) (or via the service wrapper that calls
RequestInitDataPayload) with chat_id omitted and asserts the outgoing payload
passed to self.app.invoke (Opcode.WEB_APP_INIT_DATA) does not include a chatId
field; mock or spy on service.app.invoke, capture its payload argument and
verify RequestInitDataPayload.to_payload() for the path where chat_id is None
omits chatId (use get_init_data and RequestInitDataPayload as the entrypoints to
locate code).

In `@tests/app/test_app_runtime.py`:
- Around line 155-170: Add a regression test that verifies when the app receives
a LOGIN response with token=None the existing session token is preserved: create
a RuntimeStore with SessionInfo(token="token", ...), construct a
RuntimeConnection whose startup frames include a LOGIN response whose payload
explicitly has token=None, instantiate App (or call the startup method used in
the runtime flow) with that store and connection, run the startup/invoke
sequence that processes LOGIN, then assert the store.session.token remains
"token" (unchanged) and that the connection processed the LOGIN frame; reference
runtime symbols RuntimeStore, SessionInfo, RuntimeConnection, App, and the LOGIN
response handling to locate the code paths under test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fb2c549c-b4f2-4991-b2fa-75e70edcdd9c

📥 Commits

Reviewing files that changed from the base of the PR and between 2e4e63e and f96aaeb.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • .github/workflows/publish.yml
  • docs/index.rst
  • docs/release-2-1-2.rst
  • pyproject.toml
  • src/pymax/__init__.py
  • src/pymax/api/bots/payloads.py
  • src/pymax/api/bots/service.py
  • src/pymax/app.py
  • src/pymax/types/domain/folder.py
  • src/pymax/types/domain/login.py
  • tests/api/test_chat_user_self_session_services.py
  • tests/app/test_app_runtime.py
💤 Files with no reviewable changes (1)
  • src/pymax/types/domain/folder.py

Comment on lines +21 to +25
- name: Checkout repository
uses: actions/checkout@v6

- name: Set up uv
uses: astral-sh/setup-uv@v4
- name: Install uv
uses: astral-sh/setup-uv@v8.1.0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and show the publish workflow with line numbers
FILE=".github/workflows/publish.yml"
if [ ! -f "$FILE" ]; then
  echo "Missing: $FILE"
  exit 1
fi

echo "---- $FILE (with line numbers) ----"
nl -ba "$FILE" | sed -n '1,200p'
echo "---- End ----"

# Extract all `uses:` lines for quick verification
echo "---- uses: lines ----"
rg -n "^\s*uses:\s*" "$FILE" || true

Repository: MaxApiTeam/PyMax

Length of output: 182


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/publish.yml"
if [ ! -f "$FILE" ]; then
  echo "Missing: $FILE"
  exit 1
fi

echo "---- $FILE (with line numbers) ----"
awk '{printf "%d\t%s\n", NR, $0}' "$FILE" | sed -n '1,200p'

echo "---- uses: lines ----"
rg -n "^\s*uses:\s*" "$FILE" || true

Repository: MaxApiTeam/PyMax

Length of output: 1772


🌐 Web query:

GitHub Actions security hardening pin actions to full commit SHA immutable only form movable tags

💡 Result:

Pin GitHub Actions by full-length commit SHA (immutable), not by tags (movable). GitHub’s own guidance says pinning to a full-length commit SHA is currently the only way to treat an action reference as an immutable release, and warns that specifying a tag carries risk because tags can be moved or deleted if an attacker gains access to the action’s repository.[1][2] In a workflow, use this form: - uses: owner/repo@ For example, the docs’ guidance is that the reference should be a full-length commit SHA; the short/abbreviated SHA form is explicitly discouraged as insecure.[3] Also consider enforcing this via GitHub Actions policy: - GitHub introduced an “allowed actions policy” capability to enforce SHA pinning; workflows that attempt to use an action that isn’t pinned to a full commit SHA will fail the check.[4] Why “full commit SHA only” matters (vs tags): - GitHub explains that SHA pinning helps mitigate backdoors because an attacker would need to generate a SHA-1 collision for a valid Git object payload.[1] - GitHub also notes that pinning to tags is less secure and depends on trusting the creator; even then, tags can be moved/deleted.[1][2] Practical hardening tips (beyond pinning): - Audit the action’s source code and behavior with respect to how it handles repository contents and secrets.[2][5]

Citations:


Pin all GitHub Actions uses: references to full commit SHAs in .github/workflows/publish.yml (including the OIDC publish job).

This workflow trusts movable tags for:

  • actions/checkout@v6 (line 22)
  • astral-sh/setup-uv@v8.1.0 (lines 25, 61)
  • actions/upload-artifact@v4 (line 36)
  • actions/download-artifact@v4 (line 55)

Because the publish job has id-token: write, a retagged action could tamper with what uv publish publishes. GitHub guidance recommends full-length commit SHAs for immutable action references. https://docs.github.com/en/actions/reference/security/secure-use

🧰 Tools
🪛 zizmor (1.25.2)

[warning] 21-22: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 22-22: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 25-25: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 25-25: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): this step

(cache-poisoning)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/publish.yml around lines 21 - 25, Replace all movable tag
references in the workflow with full commit SHAs: update uses: for
actions/checkout@v6, astral-sh/setup-uv@v8.1.0 (both occurrences),
actions/upload-artifact@v4, and actions/download-artifact@v4 to their
corresponding full-length commit SHAs; ensure the OIDC "publish" job's id-token:
write step also points to the SHA-pinned actions. Locate these by their uses:
lines (e.g., "uses: actions/checkout", "uses: astral-sh/setup-uv", "uses:
actions/upload-artifact", "uses: actions/download-artifact") and replace the tag
suffix (e.g., `@v6`) with the exact commit SHA obtained from each action's repo.

Comment thread src/pymax/app.py
self.store = self.config.store or SessionStore(
config.work_dir, config.session_name
)
self.store = self.config.store or SessionStore(config.work_dir, config.session_name)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve injected stores even when they are falsy.

Line 36 now treats any falsy custom store as “missing” and replaces it with a new SessionStore. That breaks the config.store injection contract and can silently switch callers from their own persistence backend to on-disk storage. Use an explicit None check here instead.

Suggested fix
-        self.store = self.config.store or SessionStore(config.work_dir, config.session_name)
+        self.store = (
+            self.config.store
+            if self.config.store is not None
+            else SessionStore(config.work_dir, config.session_name)
+        )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pymax/app.py` at line 36, The constructor currently treats any falsy
config.store as missing by using "self.config.store or SessionStore(...)", which
overwrites intentionally injected falsy stores; change the logic to an explicit
None check so injected stores are preserved: set self.store to config.store if
config.store is not None, otherwise construct a new SessionStore using
SessionStore(config.work_dir, config.session_name); update the assignment for
self.store in the initializer where self.config.store and SessionStore are
referenced.

@ink-developer ink-developer merged commit 473218a into main Jun 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant