Skip to content

Release: development → main (version fix, TSC 0.41, mypy, bug fixes)#415

Open
jacalata wants to merge 36 commits into
mainfrom
development
Open

Release: development → main (version fix, TSC 0.41, mypy, bug fixes)#415
jacalata wants to merge 36 commits into
mainfrom
development

Conversation

@jacalata

@jacalata jacalata commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Merges all accumulated development work into main for a new release.

Key changes since last main merge:

Test plan

  • pytest passes (317 passed, 2 skipped)
  • python -m black clean
  • e2e tests: 32 passed, 2 skipped, 6 pre-existing permission failures (403 on group/project admin actions — known environment limitation, not code regressions)
  • Build Windows exe and smoke test tabcmd.exe --version
  • Tag release and trigger package workflow
  • Publish to PyPI

🤖 Generated with Claude Code

jacalata and others added 30 commits June 29, 2026 15:22
…#301)

When `tabcmd login -u myuser` is run with no `-p` flag, the session
creation now correctly prompts for a password interactively (or exits
with an error if `--no-prompt` is set), rather than falling through to
the saved-session path and failing with a missing-arguments error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a user supplies -f output.csv with a URL that has a different
extension (e.g. view.png), the file type is now determined from the
user-supplied filename extension. If the filename has no recognized
extension, or no filename is given, the URL extension is used as
the fallback. Adds three tests covering the three cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace filename="tabcmd.log" in logging.basicConfig with a
RotatingFileHandler (max 1 MB per file, 5 backups) so the log file no
longer grows indefinitely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
c:\dev\tabcmd>mypy tests
tests\commands\test_projects_utils.py:22: error: Argument 1 to "_parse_project_path_to_list" of "Server" has incompatible type "None"; expected "str"  [arg-type]
tests\commands\test_user_utils.py:76: error: Incompatible types in assignment (expression has type "UserItem | None", variable has type "UserItem")  [assignment]
tests\commands\test_user_utils.py:84: error: Incompatible types in assignment (expression has type "UserItem | None", variable has type "UserItem")  [assignment]
tests\e2e\language_tests.py:208: error: "OnlineCommandTest" has no attribute "_get_workbook"  [attr-defined]
tests\e2e\tests_integration.py:68: error: Name "logger" is not defined  [name-defined]
tests\e2e\tests_integration.py:99: error: Name "logger" is not defined  [name-defined]
tests\e2e\tests_integration.py:133: error: Name "__class__" is not defined  [name-defined]
tests\commands\test_session.py:206: error: Argument 1 to "_allow_prompt" has incompatible type "Namespace"; expected "Session"  [arg-type]
tests\commands\test_session.py:211: error: Argument 1 to "_allow_prompt" has incompatible type "Namespace"; expected "Session"  [arg-type]
tests\commands\test_session.py:216: error: Argument 1 to "_allow_prompt" has incompatible type "Namespace"; expected "Session"  [arg-type]
Found 10 errors in 5 files (checked 51 source files)
output with fixes
> mypy tests
Success: no issues found in 51 source files
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Replace basicConfig (no-op if root already has handlers) with an
  explicit setLevel on the root logger
- Guard RotatingFileHandler and StreamHandler creation so repeated
  configure_log calls don't accumulate duplicate handlers
- Add tests for no-duplicate and level-set behaviour

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add test covering the case where neither filename nor URL has a
recognized extension, which should exit with an error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The elif branch now only fires when there is no active session.
If a session already exists (self.tableau_server is set), fall through
to the existing-session reuse path instead of unconditionally signing
out and prompting for a password.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Run black across all modified files
- Convert PublishCommand.run_command from @staticmethod to @classmethod
- Fix exit_with_error call passing list instead of str

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Satisfies mypy check_untyped_defs — local stub class doesn't structurally
match the expected ProjectItem type, cast() avoids changing production code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jacalata and others added 6 commits July 1, 2026 15:33
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The session reuse path checks self.user_id before calling users.get_by_id;
without it, validation always returns None and the test hits the missing-
arguments error path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: show correct version in PyInstaller exe (W-22831646)

importlib.metadata is unavailable inside PyInstaller bundles, causing
tabcmd --version to always display the hardcoded fallback. Configure
setuptools_scm to write tabcmd/_version.py at build time so PyInstaller
bundles it; version.py falls back to that file instead of a hardcoded string.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: ensure _version.py is written by doit version before pyinstaller runs

* test: verify version.py fallback import matches pyproject write_to path

* fix: use 0.0 as last-resort version fallback (clearly wrong, but parseable)

* fix: add assert narrowing to fix mypy union-attr error in test_version.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…fixes) (#416)

* Bump actions/upload-artifact from 4 to 7 (#392)

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](actions/upload-artifact@v4...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump actions/checkout from 6 to 7 (#394)

Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* ci: automate release pipeline

- release-on-merge.yml: on push to main, auto-computes next patch
  version, pushes the git tag (which triggers package.yml to build
  binaries), and creates a draft GitHub release with auto-generated
  notes for human review before publishing
- publish-pypi.yml: add release:published trigger so clicking
  "Publish release" in GitHub UI automatically uploads to PyPI;
  fix is_draft conditionals to work when triggered without inputs
- run-e2-tests.yml: add workflow_call interface (inputs + secrets)
  so the e2e suite can be called from other workflows; move
  credentials to env vars to avoid shell injection
- online_tests.py: set site_admin=False to match actual account
  permissions; add skip guard on test_delete_extract (requires
  extract encryption, not available on this site)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: remove promote:true from package.yml to preserve draft state

Binaries are now uploaded to the draft release created by
release-on-merge.yml without auto-publishing it. Release stays draft
until manually published, which then triggers PyPI upload.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: address OSS security review findings

- publish-pypi.yml: remove dead `push: tags: 'pypi'` trigger (secret
  publish path bypassing the draft-release gate); make publish
  conditionals explicit using github.event_name so the trigger surface
  is unambiguous rather than relying on null coercion
- release-on-merge.yml: filter tags to strict semver regex before
  sorting (prevents arithmetic failure on malformed tags); fail loudly
  if no prior tag exists instead of silently defaulting to v2.0.1;
  align checkout to actions/checkout@v7
- online_tests.py: suppress argv printing when login args (including
  PAT token) are present, preventing credential leak in CI logs

Tracking: #417 (PyPI trusted publishing / OIDC)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: allow coverage comment step to fail on fork PRs

MishaKav/pytest-coverage-comment@main updated and now fails with
permission denied on fork PRs (GITHUB_TOKEN is read-only for pull_request
events from forks). Coverage is still collected and reported; only the
PR comment step fails. Mark it continue-on-error so CI stays green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: make e2e permission flags configurable via workflow inputs

Previously server_admin/site_admin/project_admin/extract_encryption_enabled
were hardcoded in online_tests.py. Now they are read from env vars
(E2E_SITE_ADMIN, E2E_SERVER_ADMIN, E2E_PROJECT_ADMIN, E2E_EXTRACT_ENCRYPTION)
so callers can enable the relevant test groups for their account permissions.

workflow_dispatch: adds optional boolean choice inputs for each flag
workflow_call: adds optional boolean inputs (default false)
online_tests.py: reads flags from env via _env_bool(), defaults unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: skip e2e tests gracefully when credentials.py is absent

Previously get_login_args() raised AttributeError when credentials.py
was not present (ImportError silently set credentials = {}). Now returns
None and _test_command skips with a clear message instead of crashing.
Also removes redundant second call to get_login_args() in _test_command.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* style: black formatting for e2e test files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: fall back to E2E_* env vars in get_login_args() when credentials.py is absent

Without this, CI always skips the e2e suite because credentials.py is
not committed — even when E2E_SERVER/SITE/PATNAME/PAT are set via
workflow inputs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
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