Skip to content

Merge main into development - #490

Open
jacalata wants to merge 20 commits into
developmentfrom
jac/merge-main-into-development
Open

jacalata wants to merge 20 commits into
developmentfrom
jac/merge-main-into-development

Conversation

@jacalata

Copy link
Copy Markdown
Contributor

Brings development up to date with main (19 commits development lacked, none left over), so the development to main release PR has no conflicts.

Conflicts were resolved in favor of development, which already carries the same fixes in newer form: release-on-merge.yml, check-strings.yml, the publish --db-server handling (including the embedded-credentials fix from #458), the logger stdout/stderr split, the version.py fallback, and the renamed string keys. main's test_publish_with_creds_no_db_server is dropped because development deliberately rejects that case.

Taken from main: the saved auth_token reuse fix (#463) and its tests, macos-15-large for the x86 Mac build, setuptools==83.0.0, and the CodeQL 4.39.1 bump.

The .mo files were regenerated with doit localize and match development's, so no .mo file changes.

Merge with a merge commit, not squash. A squash would not record main's commits as merged, and the same conflicts would return on the release PR.

🤖 Generated with Claude Code

dependabot Bot and others added 20 commits June 7, 2026 21:50
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>
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>
…415)

* fix: prompt for password when username given but no password provided (#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>

* fix: --filename extension takes precedence over URL extension (#256)

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>

* fix: rotate log file using RotatingFileHandler (#211)

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>

* fix: default to https when no protocol given in server URL (#331)

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

* typing: from static methods to class methods

* type fixes: session.py

* typing: parser tests

* Update test_session.py

* Update pyproject.toml

* type fixes

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)

* fix type errors in /tests

output with fixes
> mypy tests
Success: no issues found in 51 source files

* Refactor version retrieval to use importlib.metadata

* Update tabcmd/commands/datasources_and_workbooks/export_command.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update tabcmd/commands/user/add_users_command.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix typings

* fix: set root logger level explicitly; guard against duplicate handlers

- 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>

* test: add error-path test for get_file_type_from_filename

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>

* fix: don't sign out existing session when only --username is given

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>

* style: black formatting

* style: black formatting

* style: black formatting

* ci: add workflow to check i18n string keys on PRs

* ci: pin action versions to v6, note regex limitation

* fix: black formatting and mypy errors for check_untyped_defs

- 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>

* fix: cast _ProjectItem stubs to TSC.ProjectItem in tests

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>

* chore: upgrade tableauserverclient to 0.41, add SVG format tests (#1772)

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

* fix: set user_id in test so _validate_existing_signin passes

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) (#410)

* 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>

* ci: automate release pipeline (draft release, PyPI trigger, e2e skip 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>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.37.4 to 4.37.8.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@v4.37.4...v4.37.8)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.37.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Without this, running any command after a successful `tabcmd login` falls
through to _get_saved_credentials() -> getpass.getpass(), which hangs
silently in every non-TTY context (CI, scripts, IDE terminals with no
stdin). Reattach to the auth_token in tableau_auth.json instead, and
validate it against a user-scoped endpoint so an expired token surfaces
as a fallthrough rather than a later obscure error.

Restores parity with tabcmd Classic's session-cookie reuse.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.37.8 to 4.37.9.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@v4.37.8...v4.37.9)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.37.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Bump the pip group across 1 directory 

Updates `setuptools` from 81 to 83.0.0
- [Release notes](https://github.com/pypa/setuptools/releases)
- [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst)
- [Commits](pypa/setuptools@v81.0.0...v83.0.0)

---
Did update pyinstaller but then had copilot revert pyinstaller bump to 5.13.1

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jacalata <2009720+jacalata@users.noreply.github.com>
* Require --db-server for embedded credentials/oauth workbook publishing, fix connections payload

* Translations for publish.options.db-server message

* Simplify changes

* Adjust tests
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.37.9 to 4.38.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@v4.37.9...v4.38.0)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.38.0 to 4.38.2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@v4.38.0...v4.38.2)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.38.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.38.2 to 4.38.3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@v4.38.2...v4.38.3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.38.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.38.3 to 4.39.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@v4.38.3...v4.39.0)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.39.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.39.0 to 4.39.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@v4.39.0...v4.39.1)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.39.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Brings development up to date with main before the next release. Conflicts
resolved in favor of development, which already carries the same fixes in
newer form (release-on-merge.yml, check-strings.yml, publish --db-server
handling, logger stdout/stderr split, version fallback, string keys).
Taken from main: the saved auth_token reuse fix (#463) and its tests,
macos-15-large for the x86 Mac build, setuptools 83.0.0, and the CodeQL bump.
The .mo files are regenerated with doit localize.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 26, 2026 22:17

Copilot AI left a comment

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.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@github-actions

Copy link
Copy Markdown

Coverage

Coverage Report
FileStmtsMissCoverMissing
tabcmd
   __main__.py121212 0%
   _version.py111111 0%
   tabcmd.py151515 0%
   version.py1066 40%
tabcmd/commands
   commands.py101010 0%
   constants.py771818 77%
   server.py1351818 87%
tabcmd/commands/auth
   session.py4154646 89%
tabcmd/commands/datasources_and_workbooks
   datasources_and_workbooks_command.py1571818 89%
   datasources_workbooks_views_url_parser.py14255 96%
   delete_command.py702323 67%
   export_command.py1202525 79%
   get_url_command.py1274747 63%
   publish_command.py1341616 88%
   runschedule_command.py2177 67%
tabcmd/commands/extracts
   create_extracts_command.py4288 81%
   decrypt_extracts_command.py2722 93%
   delete_extracts_command.py3766 84%
   encrypt_extracts_command.py2722 93%
   extracts.py2022 90%
   reencrypt_extracts_command.py2722 93%
   refresh_extracts_command.py481010 79%
tabcmd/commands/group
   create_group_command.py2955 83%
   delete_group_command.py2722 93%
tabcmd/commands/project
   create_project_command.py4688 83%
   delete_project_command.py3544 89%
   publish_samples_command.py3044 87%
tabcmd/commands/site
   create_site_command.py3455 85%
   delete_site_command.py2722 93%
   edit_site_command.py661818 73%
   list_command.py771212 84%
   list_sites_command.py2922 93%
tabcmd/commands/user
   add_users_command.py2955 83%
   create_site_users.py581111 81%
   create_users_command.py5999 85%
   delete_site_users_command.py4355 88%
   user_data.py2223131 86%
tabcmd/execution
   _version.py222 0%
   global_options.py14788 95%
   localize.py671111 84%
   logger_config.py6066 90%
   tabcmd_controller.py4277 83%
TOTAL296746684% 

Copilot AI left a comment

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.

🟡 Changes recommended

Session-expiration fallback and cross-server/site credential reuse remain unresolved.

2 open issues

Review effort: Lite

# Reattach to the token saved on disk by an earlier `tabcmd login`
# before falling back to _get_saved_credentials, which would prompt
# for a password we don't have. Fixes #463.
if not signed_in_object:
Comment on lines +354 to +355
self.logger.debug("Saved auth token no longer valid ({}); will re-authenticate".format(type(e).__name__))
return None

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants