diff --git a/.gitignore b/.gitignore index 90521b3b..adb4842c 100644 --- a/.gitignore +++ b/.gitignore @@ -50,4 +50,5 @@ workon test.junit.xml # exceptions -!tests/assets/detailed_users.csv \ No newline at end of file +!tests/assets/detailed_users.csv +est diff --git a/README.md b/README.md index 810f817c..3390dffa 100644 --- a/README.md +++ b/README.md @@ -5,76 +5,33 @@ [![Python tests](https://github.com/tableau/tabcmd/actions/workflows/run-tests.yml/badge.svg)](https://github.com/tableau/tabcmd/actions/workflows/run-tests.yml) [![Pypi smoke tests](https://github.com/tableau/tabcmd/actions/workflows/python-app.yml/badge.svg)](https://github.com/tableau/tabcmd/actions/workflows/python-app.yml) -An open source, cross platform command-line utility which you can use to automate site administration tasks on your Tableau Server site. +An open source, cross platform command-line utility which you can use to automate activity on Tableau Cloud or Tableau Server. -## Download exe (or rpm/deb) -* To download the latest release as an executable see https://github.com/tableau/tabcmd/releases +## Download the app +* To download the latest release ready to use see https://github.com/tableau/tabcmd/releases * There is no need to install: open a command line in the same folder as the exe and run -```shell -tabcmd [command_name] [--flags] -``` -e.g -* `tabcmd login --username [username] --password [password] --server [server_name] --site [site_name]` -* `tabcmd createproject --name [project_name]` -* `tabcmd help` -###or -## Install on the command line -(Note: this requires Python 3.7+. Generally we will actively support versions of Python that are still in security support). +> [!TIP] +> You can also download the current latest release directly on the command line: +> ```shell +> pip install tabcmd +> ``` -```shell -pip install tabcmd -``` - -Or install the current work-in-progress version from Git\ -*Only do this if you know you want the development version, no guarantee that we won't break APIs during development* -```shell -pip install git+https://github.com/tableau/tabcmd.git@development -``` - -If you go this route, but want to switch back to the non-development version, you need to run the following command before installing the stable version: +### Run tabcmd +These commands can be run from the folder that you downloaded tabcmd. If you add this folder to your PATH, they can be run from any folder. ```shell -pip uninstall tabcmd +tabcmd [command_name] [--flags] ``` +e.g +* `tabcmd login --username [username] --password [password] --server [server_name] --site [site_name]` +* `tabcmd createproject --name [project_name]` +* `tabcmd help` -### Run tabcmd - -To run tabcmd from your local copy, from a console window in the same directory as the file tabcmd.py: - -1. Clone the repo -2. Run `pip install .` - -- build -> python setup.py build - -- run tests -> pytest -- run tests against a live server -> python -m tabcmd login {your server info here} -> pytest -q tests\e2e\online_tests.py -r pfE -- with coverage calculation (https://coverage.readthedocs.io/en/6.3.2) -> coverage run -m pytest && coverage report -m - -- autoformat your code with black (https://pypi.org/project/black/) -> black --line-length 120 tabcmd tests [--check] - -- type check with mypy -> mypy tabcmd tests - -- packaging is done with pyinstaller. You can only build an executable for the platform you build on. -- To package a release, we first bump the version with `doit version` and build as 2.x.0 before packaging -> pyinstaller tabcmd\tabcmd.py --clean --noconfirm - -produces dist/tabcmd.exe -To run tabcmd during development, from a console window in the same directory as the file tabcmd.py: - -> dist/tabcmd/tabcmd.exe --help -* `python -m tabcmd.py [command_name] [--flags]` * Examples: * `tabcmd.py login --username [username] --password [password] --server [server_name] --site [site_name]` * `tabcmd.py createproject --name [project_name]` diff --git a/contributing.md b/contributing.md index 417cd22c..d8662e5a 100644 --- a/contributing.md +++ b/contributing.md @@ -13,40 +13,39 @@ * [Packaging](#packaging) -These instructions are for people who want to download the code and edit it directly. If you are interested in tabcmd but not the code, see [here](Readme.md). -####To work with tabcmd, you need to have **Python 3.7+** installed. +## Install Tabcmd +> [!NOTE] +> These instructions are for people who want to work with the python code behind tabcmd. If you are interested in tabcmd but not the code, see [here](Readme.md). +####To work with tabcmd, you need to have **Python 3.8+** installed. To propose changes, you must have a Github account. -## Contributing +### To make changes to tabcmd code -Code contributions and improvements by the community are welcomed! +Fork the tabcmd repo and create a branch off the **development** branch, not the default branch (named main) [(See Github Docs)](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) -See the LICENSE file for current open-source licensing and use information. -Before we can accept pull requests from contributors, we require a signed [Contributor License Agreement (CLA)](http://tableau.github.io/contributing.html). - +### As a library that you can use in another application: -## Development +To install the current release: +```shell +pip install tabcmd +``` -### Dev scripts -To work on the tabcmd code, use these scripts. -_(note that running mypy and black is required for code being submitted to the repo)_ +Or install the current work-in-progress version from Git\ +*Only do this if you know you want the development version, no guarantee that we won't break APIs during development* -- build -> pip install build -> python -m build -- run tests -> pytest -- run tests against a live server -> python -m tabcmd login {your server info here} -> pytest -q tests\e2e\online_tests.py -r pfE -- autoformat your code with black (https://pypi.org/project/black/) -> black . -- check types -> mypy tabcmd tests -- do test coverage calculation (https://coverage.readthedocs.io/en/6.3.2) -> bin/coverage.sh +```shell +pip install git+https://github.com/tableau/tabcmd.git@development +``` +> [!TIP] +> If you want to switch back to the non-development version, you need to run the following command before installing the stable version: +> +>```shell +>pip uninstall tabcmd +>``` + +## Software design ### Why Python? @@ -65,7 +64,7 @@ The core design principles for this app are 3. the 'commands' module contains the logic required to translate the tabcmd CLI interface into calls to tsc. This is completely dissociated from the parsers, and could theoretically be called from a completely different interface. 4. The 'execution' module is the core logic. TabcmdController gets an argparse parser, then attaches all the defined parsers to it and associates one command with each parser. -### To add a new command +#### To add a new command 0. choose the single word that will be used as your command. Let's call this one `dream` 1. add parsers/dream_parser.py, and use methods from parent_parser to define the arguments 2. add commands/dreams/dream_command.py. It must have a method run_command.py(args) and the args object must contain all information needed from the user. @@ -73,35 +72,87 @@ The core design principles for this app are 4. in map_of_commands.py, add an entry for your new command, like "dream": ("dream", DreamCommand, "Think about picnics")," 5. add tests! +## Contributing + +Code contributions and improvements by the community are welcomed! + +See the LICENSE file for current open-source licensing and use information. + +Before we can accept pull requests from contributors, we require a signed [Contributor License Agreement (CLA)](http://tableau.github.io/contributing.html). + + +## Developing +To work on the tabcmd code, use these scripts. +_(note that running mypy and black with no errors is required before code will be merged into the repo)_ + +- build and run +> pip install build +> python setup.py build +> python -m tabcmd.py [command_name] [--flags] + +- run tests +> pytest + +- run tests against a live server +> python -m tabcmd login {your server info here} +> pytest -q tests\e2e\online_tests.py -r pfE + +- autoformat your code with black (https://pypi.org/project/black/) +> black . + +- check types +> mypy tabcmd tests + +- do test coverage calculation (https://coverage.readthedocs.io/en/6.3.2) +> bin/coverage.sh + + +### Packaging +- build an executable package with [pyinstaller](https://github.com/pyinstaller/pyinstaller). +> [!NOTE] +> You can only build an executable for the platform you are running pyinstaller on. The spec for each platform is stored in tabcmd-*platform*.spec and the exact build commands for each platform can be checked in [our packaging script](.github/workflows//package.yml). + +e.g for Windows +> pyinstaller tabcmd-windows.spec --clean --noconfirm --distpath ./dist/windows + +produces dist/tabcmd.exe +To run the newly created executable, from a console window in the same directory as the file tabcmd.py: + +> dist/tabcmd/tabcmd.exe --help + + + ### Localization Strings are stored in /tabcmd/locales/[language]/*.properties by id and referred to in code as > string = _("string.id") -For runtime execution these files must be converted to .mo via .po +For runtime execution these files must be converted from .properties -> .po -> .mo files. These .mo files will be bundled in the the package by pyinstaller. The entire conversion action is done by a .doit script: > doit mo +### Versioning -## Releases -To trigger publishing to pypi tag a commit on main with 'pypi'. -When pypi-release is done, begin the app smoke test action. +Versioning is done with setuptools_scm and based on git tags. The version number will be x.y.dev0.dirty except for commits with a new version tag. + This is pulled from the git state, and to get a clean version like "v2.1.0", you must be on a commit with the tag "v2.1.0" (Creating a Github release also creates a tag on the selected branch.) +The version reflected in the executable (tabcmd -v) is stored in a metadata file created by a .doit script: +> doit version -### Versioning -Versioning is done with setuptools_scm and based on git tags. -It will be a x.y.dev0 pre-release version except for commits with a new version tag. e.g -> git tag v2.0.4 && git push --tags +## Release process -A new tag is created with the name of each release on github. +1. Create a new Github project release manually: https://docs.github.com/en/repositories/releasing-projects-on-github/about-releases + - include a useful list of changes since the last release + - include a clear list of remaining non-server-admin functional gaps -### Packaging -Before packaging, we produce a current metadata file to include in the bundle -> doit version +1. This will trigger our github packaging action to run on 3 different OSs. + - write an updated metadata file with the correct version number. + - build the python wheel + - run pyinstaller to create executables + - save the executable as an artifact on that job. -Packaging is done with pyinstaller, which will build an executable for the platform it runs on. -A github action runs on Mac, Windows and Linux to generate each executable. +1. Find the artifacts created by this job and manually copy them to the new release. (Beware! of what the file type is, github does something weird with zipping it if you download with curl etc. TODO: automate workflow with a github action) + +1. To trigger publishing to pypi run the manual workflow on main with 'pypi'. (TODO: automate trigger) + +1. When the packages are available on pypi, you can run the 'Pypi smoke test action'. This action will also be run every 24 hours to validate doing pip install. (TODO: automate the after-release trigger) -> pyinstaller tabcmd-windows.spec .... - -Packaging produces executables in the dist folder. To run: -> dist/tabcmd/tabcmd.exe --help diff --git a/pyproject.toml b/pyproject.toml index c9aa9d18..f5efb03c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ packages = ["tabcmd"] tabcmd = ["tabcmd.locales/**/*.mo"] [tool.black] line-length = 120 -target-version = ['py37', 'py38', 'py39', 'py310', 'py311'] +target-version = ['py38', 'py39', 'py310', 'py311'] extend-exclude = '^/bin/*' [tool.mypy] disable_error_code = [ @@ -29,17 +29,15 @@ dynamic = ["version"] description="A command line client for working with Tableau Server." authors = [{name="Tableau", email="github@tableau.com"}] license = {file = "LICENSE"} -readme = "README.md" +readme = "res/README.md" requires-python = ">=3.7" # https://devguide.python.org/versions/ classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12" + "Programming Language :: Python :: 3.11" ] dependencies = [ 'argparse', @@ -68,7 +66,7 @@ test = [ "pytest-runner", "requests-mock>=1.0,<2.0"] localize = ["doit", "ftfy"] -package = ["pyinstaller>=5.1", "doit"] +package = ["pyinstaller==5.13", "doit"] [project.urls] repository = "https://github.com/tableau/tabcmd" [project.scripts] diff --git a/res/README.md b/res/README.md new file mode 100644 index 00000000..0cb368a1 --- /dev/null +++ b/res/README.md @@ -0,0 +1,5 @@ + +This program is an executable command-line utility which you can use to automate activity on Tableau Cloud or Tableau Server. + +For instructions on how to use it, launch the app on a command line with 'tabcmd -h'. +For updates, bug reports and help, visit https://tableau.github.io/tabcmd/ diff --git a/tabcmd-windows.spec b/tabcmd-windows.spec index 25acf08c..e02b0bd3 100644 --- a/tabcmd-windows.spec +++ b/tabcmd-windows.spec @@ -2,7 +2,11 @@ from PyInstaller.utils.hooks import collect_data_files datas = [] -datas += collect_data_files('tabcmd.locales') +datas += collect_data_files( + 'tabcmd.locales', + include_py_files=False, + includes=["./**/tabcmd.mo"]) + print(datas) block_cipher = None @@ -22,6 +26,7 @@ a = Analysis( cipher=block_cipher, noarchive=False, ) + pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(