Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
facdf5e
Bump actions/upload-artifact from 4 to 7 (#392)
dependabot[bot] Jun 8, 2026
ed69d21
Bump actions/checkout from 6 to 7 (#394)
dependabot[bot] Jun 26, 2026
fba9519
Release: development → main (version fix, TSC 0.41, mypy, bug fixes) …
jacalata Jul 8, 2026
6310b91
Bump github/codeql-action from 4 to 4.37.4 (#445)
dependabot[bot] Aug 6, 2026
73f21ec
Bump github/codeql-action from 4.37.4 to 4.37.8 (#462)
dependabot[bot] Aug 24, 2026
d1a7aad
fix: reuse saved auth_token from tabcmd login (#463)
jacalata Aug 25, 2026
adb0efc
Bump github/codeql-action from 4.37.8 to 4.37.9 (#466)
dependabot[bot] Aug 31, 2026
ddd1b24
Bump the pip group across 1 directory with 2 updates (#461)
dependabot[bot] Sep 1, 2026
c6448dd
fix: reuse saved auth_token from tabcmd login (#463) (#465)
yyk16293 Sep 2, 2026
f4f9983
Fix publish failures when using embedded db credentials (#458)
fujihara Sep 8, 2026
b4505a4
Bump github/codeql-action from 4.37.9 to 4.38.0 (#471)
dependabot[bot] Sep 18, 2026
341772d
Bump github/codeql-action from 4.38.0 to 4.38.2 (#475)
dependabot[bot] Oct 5, 2026
d04461f
Update x86 build to macos-15 image (macos-14 deprecated now)
bcantoni Oct 5, 2026
c4e876c
Bump github/codeql-action from 4.38.2 to 4.38.3 (#478)
dependabot[bot] Oct 14, 2026
5cb536c
Update x86 build to macos-15 image (macos-14 deprecated now) (#476)
jacalata Oct 16, 2026
9a74380
Bump github/codeql-action from 4.38.3 to 4.39.0
dependabot[bot] Oct 19, 2026
a7ebda2
Bump github/codeql-action from 4.38.3 to 4.39.0 (#482)
jacalata Oct 21, 2026
2b148ae
pkg: upgrade PyInstaller 5.13.1 -> 6.22.3, drop stale hidden imports
jacalata Oct 23, 2026
d1411ba
Bump github/codeql-action from 4.39.0 to 4.39.1
dependabot[bot] Oct 26, 2026
22f3126
Bump github/codeql-action from 4.39.0 to 4.39.1 (#484)
jacalata Oct 26, 2026
313223b
ci: smoke-test packaged binaries with -v and a failed login probe
jacalata Oct 26, 2026
4510bbc
Merge remote-tracking branch 'origin/main' into jac/pyinstaller-6-upg…
jacalata Oct 26, 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
6 changes: 3 additions & 3 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ jobs:

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

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
uses: github/codeql-action/init@v4.39.1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
Expand All @@ -53,6 +53,6 @@ jobs:
# queries: security-extended,security-and-quality

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
uses: github/codeql-action/analyze@v4.39.1
with:
category: "/language:${{matrix.language}}"
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
33 changes: 29 additions & 4 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
UPLOAD_FILE_NAME: tabcmd.exe
OUT_FILE_NAME: tabcmd.exe
ASSET_MIME: application/vnd.microsoft.portable-executable
- os: macos-14-large
- os: macos-15-large
# This builds for x86
TARGET: macos
CMD_BUILD: >
Expand Down 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 @@ -89,6 +89,32 @@ jobs:
if: matrix.TARGET == 'macos'
run: ./dist/${{ matrix.TARGET }}/${{ matrix.OUT_FILE_NAME }}/Contents/MacOS/${{ matrix.APP_BINARY_FILE_NAME }}

# -v checks the bundled version file. The login probe checks that requests/urllib3/certifi are bundled:
# a closed port must fail with a connection error, not an import error.
- name: Smoke test package for ${{matrix.TARGET}}
shell: bash
env:
TARGET: ${{ matrix.TARGET }}
OUT_FILE_NAME: ${{ matrix.OUT_FILE_NAME }}
APP_BINARY_FILE_NAME: ${{ matrix.APP_BINARY_FILE_NAME }}
run: |
case "$TARGET" in
macos) BIN="./dist/macos/$OUT_FILE_NAME/Contents/MacOS/$APP_BINARY_FILE_NAME" ;;
*) BIN="./dist/$TARGET/$OUT_FILE_NAME" ;;
esac
"$BIN" -v
if out=$("$BIN" login -s https://localhost:1 -u a -p b --no-certcheck 2>&1); then
echo "$out"
echo "login to a closed port unexpectedly succeeded"
exit 1
fi
echo "$out"
if echo "$out" | grep -Eq "ImportError|ModuleNotFoundError|Unhandled exception"; then
echo "packaged binary failed with an import error"
exit 1
fi
echo "$out" | grep -q "Max retries exceeded" || { echo "expected a connection error from requests"; exit 1; }

- name: Tar app bundle for Mac
if: matrix.TARGET == 'macos'
run: |
Expand All @@ -98,7 +124,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 +138,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
6 changes: 4 additions & 2 deletions 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 All @@ -65,7 +67,7 @@ test = [
"types-setuptools"
]
localize = ["doit", "ftfy"]
package = ["doit", "pyinstaller==5.13.1", "setuptools==81"]
package = ["doit", "pyinstaller==6.22.3", "setuptools==83.0.0"]
versioning = [
"doit",
"pyinstaller_versionfile",
Expand Down
Loading
Loading