Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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