Skip to content
Open
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
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
29 changes: 20 additions & 9 deletions tests/e2e/online_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,18 @@
# config variables for test run
debug_log = "--logging-level=DEBUG"
indexing_sleep_time = 1 # wait 1 second to confirm server has indexed updates
# Flags to let us skip tests if we know we don't have the required access
server_admin = False
site_admin = True
project_admin = False
extract_encryption_enabled = False


def _env_bool(name: str, default: bool = False) -> bool:
return os.environ.get(name, str(default)).lower() == "true"


# Flags to let us skip tests if we know we don't have the required access.
# Override by setting env vars (e.g. E2E_SITE_ADMIN=true) or via workflow inputs.
server_admin = _env_bool("E2E_SERVER_ADMIN")
site_admin = _env_bool("E2E_SITE_ADMIN")
project_admin = _env_bool("E2E_PROJECT_ADMIN")
extract_encryption_enabled = _env_bool("E2E_EXTRACT_ENCRYPTION")
use_tabcmd_classic = False # toggle between testing using tabcmd 2 or tabcmd classic


Expand Down Expand Up @@ -73,11 +80,12 @@ def get_publishable_name(file_value: str) -> str:
def _test_command(test_args: list[str]):
# this will raise an exception if it gets a non-zero return code
# that will bubble up and fail the test
login_args = setup_e2e.get_login_args()
if login_args is None:
pytest.skip("No credentials available (credentials.py not found)")

# default: run tests using tabcmd 2
calling_args = (
["python", "-m", "tabcmd"] + test_args + setup_e2e.get_login_args() + [debug_log] + ["--no-certcheck"]
)
calling_args = ["python", "-m", "tabcmd"] + test_args + login_args + [debug_log] + ["--no-certcheck"]

# call the executable directly: lets us drop in classic tabcmd
if use_tabcmd_classic:
Expand All @@ -86,7 +94,8 @@ def _test_command(test_args: list[str]):
+ test_args
+ ["--no-certcheck"]
)
if database_password not in calling_args:
safe_to_print = not any(v in calling_args for v in login_args if v not in ("--server", "--site", "--token-name"))
if safe_to_print:
print(calling_args)
return subprocess.check_call(calling_args)

Expand Down Expand Up @@ -486,6 +495,8 @@ def test_refresh_ds_extract(self):

@pytest.mark.order(14)
def test_delete_extract(self):
if not extract_encryption_enabled:
pytest.skip("delete-extract requires extract encryption to be enabled on the site")
name_on_server = TestAssets.get_publishable_name(TestAssets.TDSX_FILE_WITH_EXTRACT)
TabcmdCall._delete_extract(name_on_server, "-d")

Expand Down
5 changes: 5 additions & 0 deletions tests/e2e/setup_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@

try:
from tests.e2e import credentials # type: ignore

_has_credentials = True
except ImportError:
credentials = {} # type: ignore
_has_credentials = False

our_program = "tabcmd.exe"
launch_path = os.path.join("dist", "tabcmd")
Expand Down Expand Up @@ -46,6 +49,8 @@ def get_executable():


def get_login_args():
if not _has_credentials:
return None
return [
"--server",
Comment on lines 51 to 55
credentials.server,
Expand Down
Loading