Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ff57dc6
fix: prompt for password when username given but no password provided…
jacalata Jun 29, 2026
a9836b0
fix: --filename extension takes precedence over URL extension (#256)
jacalata Jun 30, 2026
652f0a3
fix: rotate log file using RotatingFileHandler (#211)
jacalata Jun 30, 2026
304b9eb
fix: default to https when no protocol given in server URL (#331)
jacalata Jun 30, 2026
ae9f929
typing: from static methods to class methods
jacalata Aug 14, 2025
ad78386
type fixes: session.py
jacalata Aug 14, 2025
c8901ae
typing: parser tests
jacalata Aug 14, 2025
daee090
Update test_session.py
jacalata Aug 14, 2025
4cb5e68
Update pyproject.toml
jacalata Aug 14, 2025
3c8f7c5
type fixes
jacalata Aug 14, 2025
366c39a
fix type errors in /tests
jacalata Sep 24, 2025
0600fab
Refactor version retrieval to use importlib.metadata
jacalata Feb 13, 2026
5d7247a
Update tabcmd/commands/datasources_and_workbooks/export_command.py
jacalata Apr 13, 2026
ac057f3
Update tabcmd/commands/user/add_users_command.py
jacalata Apr 13, 2026
b202848
fix typings
jacalata Apr 21, 2026
152cb11
fix: set root logger level explicitly; guard against duplicate handlers
jacalata Jun 30, 2026
c7b08ae
test: add error-path test for get_file_type_from_filename
jacalata Jun 30, 2026
8ab8048
fix: don't sign out existing session when only --username is given
jacalata Jun 30, 2026
316c23b
style: black formatting
jacalata Jun 30, 2026
52ff6b2
style: black formatting
jacalata Jun 30, 2026
7e24c46
style: black formatting
jacalata Jun 30, 2026
7b3d329
ci: add workflow to check i18n string keys on PRs
jacalata Jun 30, 2026
2b30522
ci: pin action versions to v6, note regex limitation
jacalata Jun 30, 2026
f0de017
fix: black formatting and mypy errors for check_untyped_defs
jacalata Jul 1, 2026
f278a62
ci: add workflow to check i18n string keys on PRs (#409)
jacalata Jul 1, 2026
001c045
fix: cast _ProjectItem stubs to TSC.ProjectItem in tests
jacalata Jul 1, 2026
6d0b0ac
fix: add rotating log file handler (#211) (#404)
jacalata Jul 1, 2026
096819c
fix: prompt for password when --username given without credentials (#…
jacalata Jul 1, 2026
02e9aba
fix: use explicit extension check in get_file_type_from_filename (#25…
jacalata Jul 1, 2026
d50c35f
fix: default to https when no scheme specified in server URL (#331) (…
jacalata Jul 1, 2026
1ec12f9
mypy: enable checking untyped code (#367)
jacalata Jul 1, 2026
e447579
chore: upgrade tableauserverclient to 0.41, add SVG format tests (#1772)
jacalata Jun 29, 2026
00c09e9
fix: set user_id in test so _validate_existing_signin passes
jacalata Jul 1, 2026
9686e99
chore: upgrade tableauserverclient to 0.41 (#408)
jacalata Jul 1, 2026
ec299d4
fix: show correct version in PyInstaller exe (W-22831646) (#410)
jacalata Jul 2, 2026
7248112
ci: automate release pipeline (draft release, PyPI trigger, e2e skip …
jacalata Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/check-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }}
uses: actions/setup-python@v6
Expand Down Expand Up @@ -46,6 +46,7 @@ jobs:

- name: Comment on pull request with coverage
uses: MishaKav/pytest-coverage-comment@main
continue-on-error: true
with:
pytest-coverage-path: ./pytest-coverage.txt
# TODO: check if this has been fixed
Expand Down
28 changes: 28 additions & 0 deletions .github/workflows/check-strings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Check i18n strings

on:
workflow_dispatch:
pull_request:
branches:
- development
- main

permissions:
contents: read

jobs:
check-strings:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v6

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.12'

# Note: check_strings.py uses regex matching and will not catch dynamic keys
# (f-strings, variables, concatenation). This is a best-effort check.
- name: Check for missing i18n string keys
run: python bin/i18n/check_strings.py
2 changes: 1 addition & 1 deletion .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v7

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/generate-metadata.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- name: Set up Python
uses: actions/setup-python@v6
Expand Down
5 changes: 2 additions & 3 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:
UPLOAD_FILE_NAME: tabcmd

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- uses: actions/setup-python@v6
with:
Expand Down Expand Up @@ -98,7 +98,7 @@ jobs:


- name: Upload build artifact for ${{ matrix.TARGET }}
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: tabcmd-${{ matrix.TARGET }}
path: ./dist/${{ matrix.TARGET }}/${{ matrix.UPLOAD_FILE_NAME }}
Expand All @@ -112,4 +112,3 @@ jobs:
file: ./dist/${{ matrix.TARGET }}/${{ matrix.UPLOAD_FILE_NAME }}
tag: ${{ github.ref_name }}
overwrite: true
promote: true
10 changes: 5 additions & 5 deletions .github/workflows/publish-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ on:
description: 'Test PyPi'
required: true
type: boolean
push:
tags: 'pypi'
release:
types: [published]

jobs:
build-n-publish:
name: Build dist files for PyPi
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-python@v6
Expand All @@ -32,13 +32,13 @@ jobs:
python -m build

- name: Publish distribution 📦 to Test PyPI
if: ${{ inputs.is_draft }}
if: ${{ github.event_name == 'workflow_dispatch' && inputs.is_draft == true }}
uses: pypa/gh-action-pypi-publish@release/v1 # license BSD-2
with:
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
repository_url: https://test.pypi.org/legacy/
- name: Publish distribution 📦 to PyPI
if: ${{ !inputs.is_draft }}
if: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.is_draft != true) }}
uses: pypa/gh-action-pypi-publish@release/v1 # license BSD-2
with:
password: ${{ secrets.PYPI_API_TOKEN }}
60 changes: 60 additions & 0 deletions .github/workflows/release-on-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Draft release on merge to main

# Triggers when a PR is merged into main (push to main covers all merge strategies).
# Creates a draft GitHub release with auto-generated notes and pushes the version tag.
# The tag push triggers package.yml to build and attach binaries.
# When you publish the draft release, publish-pypi.yml uploads to PyPI automatically.

on:
push:
branches:
- main

permissions:
contents: write

jobs:
draft-release:
name: Create draft release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0

- name: Compute next patch version
id: version
run: |
LAST_TAG=$(git tag --list | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1)
if [ -z "$LAST_TAG" ]; then
echo "No prior semver tag found; cannot auto-increment version." >&2
exit 1
fi
BASE="${LAST_TAG#v}"
MAJOR=$(echo "$BASE" | cut -d. -f1)
MINOR=$(echo "$BASE" | cut -d. -f2)
PATCH=$(echo "$BASE" | cut -d. -f3)
NEXT="v${MAJOR}.${MINOR}.$((PATCH + 1))"
echo "tag=$NEXT" >> "$GITHUB_OUTPUT"
echo "last_tag=$LAST_TAG" >> "$GITHUB_OUTPUT"

- name: Push version tag
env:
TAG: ${{ steps.version.outputs.tag }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag "$TAG"
git push origin "$TAG"

- name: Create draft release with auto-generated notes
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.version.outputs.tag }}
LAST_TAG: ${{ steps.version.outputs.last_tag }}
run: |
gh release create "$TAG" \
--draft \
--title "$TAG" \
--generate-notes \
--notes-start-tag "$LAST_TAG"
69 changes: 66 additions & 3 deletions .github/workflows/run-e2-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,60 @@ on:
required: true
pat:
required: true
server_admin:
description: 'Account has server admin role'
required: false
default: 'false'
type: choice
options: ['false', 'true']
site_admin:
description: 'Account has site admin role'
required: false
default: 'false'
type: choice
options: ['false', 'true']
project_admin:
description: 'Account has project admin role'
required: false
default: 'false'
type: choice
options: ['false', 'true']
extract_encryption_enabled:
description: 'Site has extract encryption enabled'
required: false
default: 'false'
type: choice
options: ['false', 'true']
workflow_call:
inputs:
server:
required: true
type: string
site:
required: true
type: string
patname:
required: true
type: string
server_admin:
required: false
type: boolean
default: false
site_admin:
required: false
type: boolean
default: false
project_admin:
required: false
type: boolean
default: false
extract_encryption_enabled:
required: false
type: boolean
default: false
secrets:
pat:
required: true

jobs:
build:
Expand All @@ -23,7 +77,7 @@ jobs:
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }}
uses: actions/setup-python@v6
Expand All @@ -42,6 +96,15 @@ jobs:
pip install .[test]

- name: Run e2e tests
run: |
python -m tabcmd login --server "${{ github.event.inputs.server }}" --site "${{ github.event.inputs.site }}" --token-name "${{ github.event.inputs.patname }}" --token-value "${{ github.event.inputs.pat }}"
env:
E2E_SERVER: ${{ inputs.server }}
E2E_SITE: ${{ inputs.site }}
E2E_PATNAME: ${{ inputs.patname }}
E2E_PAT: ${{ secrets.pat || inputs.pat }}
E2E_SERVER_ADMIN: ${{ inputs.server_admin || 'false' }}
E2E_SITE_ADMIN: ${{ inputs.site_admin || 'false' }}
E2E_PROJECT_ADMIN: ${{ inputs.project_admin || 'false' }}
E2E_EXTRACT_ENCRYPTION: ${{ inputs.extract_encryption_enabled || 'false' }}
run: |
python -m tabcmd login --server "$E2E_SERVER" --site "$E2E_SITE" --token-name "$E2E_PATNAME" --token-value "$E2E_PAT"
pytest -q tests/e2e/online_tests.py -r pfE
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }}
uses: actions/setup-python@v6
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
.idea
*.DS_Store

# setuptools_scm generated version file (may appear in any subpackage)
tabcmd/**/_version.py
tabcmd/_version.py

# python build files
build/
dist/
Expand Down
8 changes: 8 additions & 0 deletions dodo.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,14 @@ def write_for_pyinstaller():
version=numeric_version,
)

# Write tabcmd/_version.py so PyInstaller bundles the correct version string.
# setuptools_scm only writes this file during pip install/build; doit version
# may run in CI without a prior install step.
version_file = os.path.join("tabcmd", "_version.py")
with open(version_file, "w", encoding="utf-8") as f:
f.write("# generated by doit version -- do not edit\n")
f.write("version = {!r}\n".format(version))

return {
"actions": [write_for_pyinstaller],
"verbosity": 2,
Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ requires = ["build", "setuptools", "setuptools_scm"]
build-backend = "setuptools.build_meta"
[tool.setuptools_scm]
local_scheme = "no-local-version" # require pypi supported versions always
write_to = "tabcmd/_version.py"
[tool.setuptools]
packages = ["tabcmd"]
[tool.setuptools.package-data]
Expand All @@ -13,6 +14,7 @@ required-version = 22
target-version = ['py310', 'py311']
extend-exclude = '^/bin/*'
[tool.mypy]
check_untyped_defs = true
disable_error_code = [
'misc',
'import'
Expand Down Expand Up @@ -46,7 +48,7 @@ dependencies = [
"appdirs",
"requests>=2.25,<3.0",
"setuptools",
"tableauserverclient==0.40",
"tableauserverclient==0.41",
"urllib3",
]
[project.optional-dependencies]
Expand Down
6 changes: 3 additions & 3 deletions tabcmd/commands/auth/login_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ def define_args(parser):
# just uses global options
pass

@staticmethod
def run_command(args):
logger = log(__class__.__name__, args.logging_level)
@classmethod
def run_command(cls, args):
logger = log(cls.__name__, args.logging_level)
logger.debug(_("tabcmd.launching"))
session = Session()
session.create_session(args, logger)
6 changes: 3 additions & 3 deletions tabcmd/commands/auth/logout_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ def define_args(parser):
# has no options
pass

@staticmethod
def run_command(args):
logger = log(__class__.__name__, args.logging_level)
@classmethod
def run_command(cls, args):
logger = log(cls.__name__, args.logging_level)
logger.debug(_("tabcmd.launching"))
session = Session()
session.end_session_and_clear_data()
Loading
Loading