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
32 changes: 20 additions & 12 deletions .github/workflows/release_actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,21 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # setuptools-scm needs full git history

- name: Install uv
uses: astral-sh/setup-uv@v4

- name: Install pyinstaller
- name: Install package and dependencies
shell: bash
run: python -m pip install pyinstaller
run: |
uv pip install --system pyinstaller
uv pip install --system "commcare-export[executable]"

- name: Generate exe
shell: bash
run: |
pip install commcare-export
pip install -r build_exe/requirements.txt
pyinstaller --dist ./dist/linux commcare-export.spec
run: pyinstaller --dist ./dist/linux commcare-export.spec

- name: Upload release assets
uses: AButler/upload-release-assets@v3.0
Expand All @@ -34,17 +38,21 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # setuptools-scm needs full git history

- name: Install uv
uses: astral-sh/setup-uv@v4

- name: Install pyinstaller
- name: Install package and dependencies
shell: pwsh
run: python -m pip install pyinstaller
run: |
uv pip install --system pyinstaller
uv pip install --system "commcare-export[executable]"

- name: Generate exe
shell: pwsh
run: |
pip install commcare-export
pip install -r build_exe/requirements.txt
pyinstaller --dist ./dist/windows commcare-export.spec
run: pyinstaller --dist ./dist/windows commcare-export.spec

- name: Upload release assets
uses: AButler/upload-release-assets@v3.0
Expand Down
71 changes: 55 additions & 16 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,28 +35,39 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 50
- run: git fetch --tags origin # So we can use git describe. actions/checkout@v4 does not pull tags.
fetch-depth: 0 # setuptools-scm needs full git history

# MySQL set up
- run: sudo service mysql start # Ubuntu already includes mysql no need to use service
- run: sudo service mysql start
- run: mysql -uroot -proot -e "CREATE USER '${{ env.DB_USER }}'@'%';"
- run: mysql -uroot -proot -e "GRANT ALL PRIVILEGES ON *.* TO '${{ env.DB_USER }}'@'%';"

- uses: actions/setup-python@v5
# Install uv
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true
cache-dependency-glob: "pyproject.toml"

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'

- run: sudo apt-get install pandoc
- run: pip install --upgrade pip
- run: pip install setuptools
- run: python setup.py sdist
- run: pip install dist/*
- run: pip install pymysql psycopg2 pyodbc
- run: pip install coverage coveralls
- run: pip install mypy
- run: pip install pytest
- run: pip install -e ".[test]"

# Build and install package
- name: Build distribution
run: uv build

- name: Install package with extras
run: |
uv pip install --system dist/*.whl
uv pip install --system pymysql psycopg2 pyodbc
uv pip install --system coverage coveralls
uv pip install --system mypy
uv pip install --system pytest
uv pip install --system -e ".[test]"

# Run tests and save test coverage
- run: coverage run -m pytest
Expand All @@ -66,8 +77,10 @@ jobs:
MSSQL_URL: mssql+pyodbc://sa:${{ env.DB_PASSWORD }}@localhost/
HQ_USERNAME: ${{ secrets.HQ_USERNAME }}
HQ_API_KEY: ${{ secrets.HQ_API_KEY }}

# Convert coverage data for Coveralls
- run: coverage lcov -o coverage/lcov.info

# Use Coveralls to track coverage
- name: Coveralls
uses: coverallsapp/github-action@v2
Expand All @@ -76,8 +89,34 @@ jobs:
flag-name: run-${{ join(matrix.*, '-') }}
github-token: ${{ secrets.GITHUB_TOKEN }}

# Check typing
- run: mypy --install-types --non-interactive commcare_export/ tests/ migrations/ setup.py
typing:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true
cache-dependency-glob: "pyproject.toml"

- name: Set up Python 3.13
uses: actions/setup-python@v5
with:
python-version: '3.13'

- name: Build distribution
run: uv build

- name: Install package with mypy
run: |
uv pip install --system dist/*.whl
uv pip install --system mypy
uv pip install --system -e ".[test]"

- run: mypy --install-types --non-interactive commcare_export/ tests/ migrations/

finish:
needs: test
Expand Down
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,11 @@ nosetests.xml

# Excel
~*.xlsx
commcare_export.log
commcare_export.log

# uv
.uv/
uv.lock

# Build artifacts
*.whl
39 changes: 19 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ $ venv

## Install CommCare Export

Install CommCare Export via `pip`
[uv](https://docs.astral.sh/uv/) is a fast Python package installer and resolver.

```shell
$ pip install commcare-export
$ uv pip install commcare-export
```

## CommCare HQ
Expand Down Expand Up @@ -563,28 +563,27 @@ via the `--output-format <format>` option, and it can be directed to a file with
Dependencies
------------

Required dependencies will be automatically installed via pip. But since
you may not care about all export formats, the various dependencies there
are optional. Here is how you might install them:
Required dependencies will be automatically installed. Optional dependencies
for specific export formats can be installed as extras:

```shell
# To export "xlsx"
$ pip install "commcare-export[xlsx]"
$ uv pip install "commcare-export[xlsx]"

# To export "xls"
$ pip install "commcare-export[xls]"
$ uv pip install "commcare-export[xls]"

# To sync with a Postgres database
$ pip install "commcare-export[postgres]"
$ uv pip install "commcare-export[postgres]"

# To sync with a mysql database
$ pip install "commcare-export[mysql]"
$ uv pip install "commcare-export[mysql]"

# To sync with a database which uses odbc (e.g. mssql)
$ pip install "commcare-export[odbc]"
$ uv pip install "commcare-export[odbc]"

# To sync with another SQL database supported by SQLAlchemy
$ pip install "commcare-export[base_sql]"
$ uv pip install "commcare-export[base_sql]"
# Then install the Python package for your database
```

Expand All @@ -598,11 +597,11 @@ Contributing
2\. Clone your fork, install into a virtualenv, and start a feature branch

```shell
$ git clone git@github.com:dimagi/commcare-export.git
$ git clone git@github.com:your-username/commcare-export.git
$ cd commcare-export
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install -e ".[test]"
$ uv venv
$ source .venv/bin/activate # On Windows: .venv\Scripts\activate
$ uv pip install -e ".[test]"
$ git checkout -b my-super-duper-feature
```

Expand Down Expand Up @@ -651,18 +650,18 @@ $ git tag -a "X.YY.0" -m "Release X.YY.0"
$ git push --tags
```

2\. Create the source distribution
2\. Create the distribution

```shell
$ python setup.py sdist
$ uv build
```
Ensure that the archive (`dist/commcare-export-X.YY.0.tar.gz`) has the correct version number (matching the tag name).

Ensure that the archives in `dist/` have the correct version number (matching the tag name).

3\. Upload to pypi

```shell
$ pip install twine
$ twine upload -u dimagi dist/commcare-export-X.YY.0.tar.gz
$ uv publish
```

4\. Verify upload
Expand Down
58 changes: 21 additions & 37 deletions commcare_export/version.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,36 @@
import io
import re
import os.path
import subprocess
"""Version information for commcare-export.

__all__ = ['__version__', 'stored_version', 'git_version']
The version is managed by setuptools-scm and stored in the VERSION file.
"""
from pathlib import Path

VERSION_PATH = os.path.join(os.path.dirname(__file__), 'VERSION')
__all__ = ['__version__']

VERSION_PATH = Path(__file__).parent / 'VERSION'

def stored_version():
if os.path.exists(VERSION_PATH):
with io.open(VERSION_PATH, encoding='ascii') as fh:
return fh.read().strip()
else:
return None

def get_version():
"""Read version from VERSION file written by setuptools-scm during build.

def git_version():
if os.environ.get('DET_EXECUTABLE'):
return None

described_version_bytes = subprocess.Popen(
['git', 'describe'],
stdout=subprocess.PIPE
).communicate()[0].strip()
version_raw = described_version_bytes.decode('ascii')
return parse_version(version_raw)


def parse_version(version_raw):
"""Attempt to convert a git version to a version
compatible with PEP440: https://peps.python.org/pep-0440/
For development installs, setuptools-scm handles version detection automatically.
For built distributions, the version is in the VERSION file.
"""
match = re.match('(\d+\.\d+\.\d+)(?:-(\d+).*)?', version_raw)
if match:
tag_version, lead_count = match.groups()
if lead_count:
tag_version += f".dev{lead_count}"
return tag_version
if VERSION_PATH.exists():
return VERSION_PATH.read_text(encoding='ascii').strip()

return version_raw
# During development with editable install, try to get version from setuptools-scm
try:
from setuptools_scm import get_version as scm_get_version
return scm_get_version(root='..', relative_to=__file__)
except Exception:
pass

# Final fallback for edge cases (e.g., PyInstaller executable)
return "unknown"

def version():
return stored_version() or git_version()

__version__ = get_version()

__version__ = version()

if __name__ == '__main__':
print(__version__)
Loading
Loading