{"id":15442937,"url":"https://github.com/tock/tockloader","last_synced_at":"2025-10-09T16:08:10.351Z","repository":{"id":14531101,"uuid":"76670375","full_name":"tock/tockloader","owner":"tock","description":"Tool for programming Tock onto hardware boards.","archived":false,"fork":false,"pushed_at":"2025-10-06T21:10:42.000Z","size":1544,"stargazers_count":43,"open_issues_count":7,"forks_count":54,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-10-06T23:21:18.899Z","etag":null,"topics":["pip","python-3","tock"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tock.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2016-12-16T16:56:22.000Z","updated_at":"2025-10-06T21:08:54.000Z","dependencies_parsed_at":"2023-01-13T17:59:36.060Z","dependency_job_id":"51928c36-40fb-43b5-9856-10676bad426c","html_url":"https://github.com/tock/tockloader","commit_stats":{"total_commits":624,"total_committers":32,"mean_commits":19.5,"dds":"0.17147435897435892","last_synced_commit":"2a500cb378eaf415da6ed7f2cef9bc1ee43b38df"},"previous_names":["helena-project/tock-loader"],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/tock/tockloader","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tock%2Ftockloader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tock%2Ftockloader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tock%2Ftockloader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tock%2Ftockloader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tock","download_url":"https://codeload.github.com/tock/tockloader/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tock%2Ftockloader/sbom","scorecard":{"id":667484,"data":{"date":"2025-08-11","repo":{"name":"github.com/tock/tockloader","commit":"eb405cd61142306b97d294aed8391fd0a69157df"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.3,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":10,"reason":"13 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 2/26 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/autoblack.yml:1","Warn: no topLevel permission defined: .github/workflows/tockloader.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/autoblack.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/tock/tockloader/autoblack.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/autoblack.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/tock/tockloader/autoblack.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tockloader.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/tock/tockloader/tockloader.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tockloader.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/tock/tockloader/tockloader.yml/master?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/autoblack.yml:19","Warn: pipCommand not pinned by hash: .github/workflows/tockloader.yml:22","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Vulnerabilities","score":9,"reason":"1 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-wj6h-64fc-37mp"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 6 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'","Warn: branch protection not enabled for branch 'v1.7.0'","Warn: branch protection not enabled for branch 'v0.7.x'","Warn: branch protection not enabled for branch 'v0.6.x'","Warn: branch protection not enabled for branch 'v0.5.x'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-21T18:36:09.449Z","repository_id":14531101,"created_at":"2025-08-21T18:36:09.449Z","updated_at":"2025-08-21T18:36:09.449Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279001762,"owners_count":26083171,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pip","python-3","tock"],"created_at":"2024-10-01T19:32:01.191Z","updated_at":"2025-10-09T16:08:10.345Z","avatar_url":"https://github.com/tock.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ![TockLoader](http://www.tockos.org/assets/img/tockloader.svg#a \"Tockloader Logo\")\n\nTool for programming Tock onto hardware boards.\n\nInstall\n-------\n\n```\npip3 install pipx\npipx install tockloader\n```\n\nIf you want tab completions:\n\n```\nregister-python-argcomplete tockloader \u003e\u003e ~/.bashrc\n```\n\nUsage\n-----\n\nThis tool installs a binary called `tockloader`, which supports several commands.\n\n### Primary Commands\n\nThese are the main commands for managing apps on a board.\n\n#### `tockloader install`\n\nLoad Tock applications on to the board. Use `--no-replace` to install\nmultiple copies of the same app.\n\n#### `tockloader update`\n\nUpdate an application that is already flashed to the board with a new\nbinary.\n\n#### `tockloader uninstall [application name(s)]`\n\nRemove an application from flash by its name.\n\n\n### Board Inspection Commands\n\nThese query the board for its current state.\n\n#### `tockloader list`\n\nPrint information about the apps currently loaded onto the board.\n\n#### `tockloader info`\n\nShow all properties of the board.\n\n\n### Utility Commands\n\nThese provide other helpful features.\n\n#### `tockloader listen`\n\nListen to UART `printf()` data from a board. Use the option `--rtt` to use\nSegger's RTT listener instead of using a serial port.\n\n\n### Other Commands\n\nThese provide more internal functionality.\n\n#### `tockloader flash`\n\nLoad binaries onto hardware platforms that are running a compatible bootloader.\nThis is used by the [TockOS](https://github.com/helena-project/tock) Make system\nwhen kernel binaries are programmed to the board with `make program`.\n\n#### `tockloader inspect-tab`\n\nShow details about a compiled TAB file.\n\n#### `tockloader enable-app [application name(s)]`\n\nEnable an app so that the kernel will run it at boot.\n\n#### `tockloader disable-app [application name(s)]`\n\nDisable an app so that the kernel will not start it at boot.\n\n#### `tockloader sticky-app [application name(s)]`\n\nMark an app as sticky so that the `--force` flag is required to uninstall it.\n\n#### `tockloader unsticky-app [application name(s)]`\n\nRemove the sticky flag from an app.\n\n#### `tockloader list-attributes`\n\nShow all of the attributes that are stored on the board.\n\n#### `tockloader set-attribute [attribute key] [attribute value]`\n\nSet a particular attribute key to the specified value. This will overwrite\nan existing attribute if the key matches.\n\n#### `tockloader remove-attribute [attribute key]`\n\nRemove a particular attribute from the board.\n\n#### `tockloader dump-flash-page [page number]`\n\nShow the contents of a page of flash.\n\n#### `tockloader read [address] [# bytes]`\n\nRead arbitrary flash memory from the board.\n\n#### `tockloader write [address] [# bytes] [value]`\n\nWrite arbitrary flash memory on the board with a specific value.\n\n#### `tockloader list-known-boards`\n\nPrint which boards tockloader has default settings for built-in.\n\n#### `tockloader set-start-address [address]`\n\nSet the jump address the bootloader uses for the location of the kernel.\n\n#### `tockloader local-board set [board]`\n\nSet the name of the board to use with a local binary file instead of hardware.\n\n#### `tockloader local-board unset`\n\nRemove the board to use with a local binary file instead of hardware.\n\n#### `tockloader local-board flush`\n\nWrite the local binary file to the hardware board.\n\n#### `tockloader tbf tlv add|modify|delete [TLVNAME]`\n\nInteract with TLV structures within a TBF.\n\n#### `tockloader tbf credential add|delete [credential type]`\n\nAdd and remove credentials in the TBF footer.\n\n#### `tockloader tbf convert [output format]`\n\nConvert a TBF to a different format.\n\n#### `tockloader tickv get|append|invalidate|dump|cleanup|reset [key] [value]`\n\nInteract with a TicKV key-value database.\n\n\nSpecifying the Board\n--------------------\n\nFor tockloader to know how to interface with a particular hardware board,\nit tries several options:\n\n1. Read the parameters from the bootloader. Tockloader assumes it can open a\n   serial connection to a\n   [tock-bootloader](https://github.com/tock/tock-bootloader/) on the board.\n\n2. Use `JLinkExe`, `OpenOCD`, `STLink`, or the default virtual board to discover\n   known boards.\n\n3. Use the `--board` command line flag and a list of known boards.\n\n4. Use individual command line flags that specify how to interact with the\n   board.\n\nIf command line flags are passed they take priority over any automatically\ndiscovered options.\n\nTockloader has hardcoded parameters for a variety of boards. You can list these\nwith:\n\n    tockloader list-known-boards\n\nTo use a known board, if it is not automatically discovered, you can:\n\n    tockloader [command] --board [board]\n\nIf your board is not a known board, you can specify the required parameters\nvia command line options. Note, you also need to provide a name for the board.\n\n    tockloader [command] --board [board] --arch [arch] --page-size [page_size]\n\n- `board`: The name of the board. This helps prevent incompatible applications\n  from being flashed on the wrong board.\n- `arch`: The architecture of the board. Likely `cortex-m0` or `cortex-m4`.\n- `page_size`: The size in bytes of the smallest erasable unit in flash.\n\nSpecifying the Communication Channel\n------------------------------------\n\nTockloader defaults to using a serial connection to an on-chip bootloader to\nprogram and interact with a board. If you need to use a different communication\nmechanism, you can specify what Tockloader should use with command line\narguments. Note, Tockloader's board autodiscovery process also selects different\ncommunication channels based on which board it finds.\n\nTo use a JTAG interface using JLinkExe, specify `--jlink`. JLinkExe requires\nknowing the device type of the MCU on the board.\n\n    tockloader [command] --board [board] --arch [arch] --page-size [page_size] \\\n                         --jlink --jlink-cmd [jlink_cmd] --jlink-device [device] \\\n                         --jlink-speed [speed] --jlink-if [if] \\\n                         --jlink-serial-number [serial_number]\n\n- `jlink_cmd`: The JLink executable to invoke. Defaults to `JLinkExe` on\n  Mac/Linux, and `JLink` on Windows.\n- `device`: The JLinkExe device identifier.\n- `speed`: The speed value to pass to JLink. Defaults to 1200.\n- `if`: The interface to pass to JLink.\n- `serial-number`: The serial number of the target board to use with JLink.\n\nTockloader can also do JTAG using OpenOCD. OpenOCD needs to know which config\nfile to use.\n\n    tockloader [command] --board [board] --arch [arch] --page-size [page_size] \\\n                         --openocd --openocd-board [openocd_board] \\\n                         --openocd-cmd [openocd_cmd] \\\n                         --openocd-options [openocd_options] \\\n                         --openocd-commands [openocd_commands]\n\n- `openocd_board`: The `.cfg` file in the board folder in OpenOCD to use.\n- `openocd_cmd`: The OpenOCD executable to invoke. Defaults to `openocd`.\n- `openocd_options`: A list of Tock-specific flags used to customize how\n  Tockloader calls OpenOCD based on experience with various boards and their\n  quirks. Options include:\n    - `noreset`: Removes the command `reset init;` from OpenOCD commands.\n    - `nocmdprefix`: Removes the commands `init; reset init; halt;` from OpenOCD\n      commands.\n    - `workareazero`: Adds the command `set WORKAREASIZE 0;` to OpenOCD commands.\n    - `resume`: Adds the commands `soft_reset_halt; resume;` to OpenOCD commands.\n- `openocd_commands`: This sets a custom OpenOCD command string to allow\n  Tockloader to program arbitrary chips with OpenOCD before support for the\n  board is officially include in Tockloader. The following main operations can\n  be customized:\n    - `program`: Operation used to write a binary to the chip.\n    - `read`: Operation used to read arbitrary flash memory on the chip.\n    - `erase`: Operation that erases arbitrary ranges of flash memory on the chip.\n\n    The custom values are specified as key=value pairs, for example,\n    `--openocd_commands 'program=write_image; halt;' 'erase=flash fillb\n    {address:#x} 0xff 512;'`. Operation strings can include wildcards which will\n    get set with the correct value by Tockloader:\n    - `{{binary}}`: The binary file path.\n    - `{address:#x}`: The specified address for the binary to be programmed at.\n    - `{length}`: The number of bytes. Only valid for the `read` operation.\n\nFor STM32 boards, Tockloader supports\n[STLINK](https://github.com/stlink-org/stlink). The stlink tool knows how to\ninterface with the boards, so there are not many flags.\n\n    tockloader [command] --board [board] --arch [arch] --page-size [page_size] \\\n                         --stlink \\\n                         --stinfo-cmd [stinfo_cmd] --stflash-cmd [stflash_cmd]\n\n- `stinfo_cmd`: The st-info executable to invoke. Defaults to `st-info`.\n- `stflash_cmd`: The st-flash executable to invoke. Defaults to `st-flash`.\n\nFinally, Tockloader can treat a local file as though it were the flash contents\nof a board. The file can then be loaded separately onto a board.\n\n    tockloader [command] --flash-file [filepath]\n\n- `filepath`: The file to use as the flash contents. Will be created if it\n  doesn't exist.\n\nTockloader can use a flash file by default. This is particularly helpful for\nvirtual, QEMU-based boards, or other boards that do not support fine-grained\nreading and writing. You can set the default local board to use:\n\n    tockloader local-board set [board]\n\nIf the board you are using is new or not a known board in tockloader, you can\nmanually specifying the necessary parameters:\n\n    tockloader local-board set [board] --arch [arch] --app-address [address] --flash-address [address] --flush-command [command] --binary-path [path]\n\n- `board`: Name of the board you want to use.\n- `arch`: Name of the architecture the board uses.\n- `app-address`: The flash address where apps start.\n- `flash-address`: The address where flash starts.\n- `flush-command`: The command to write the file to the board. Use `{binary}` to\n  specify where the file path should go in the command.\n- `binary-path`: Where the actual flash-file binary should be located. Optional.\n  If not provided defaults to a suitable tockloader application data\n  directory.\n\nThen, tockloader commands will use a virtual image file for all operations that\ninteract with a board.\n\nYou can reverse this with:\n\n    tockloader local-board unset\n\nExample Usage\n-------------\n\nInstall an app, make sure it's up to date, and make sure it's the only app on\nthe board:\n\n    tockloader install --make --erase\n\nGet all info from the board that can be used to help debugging:\n\n    tockloader info\n\nPrint additionally debugging information. This can be helpful when using JTAG.\n\n    tockloader install --debug\n\nGet `printf()` data from a board:\n\n    tockloader listen\n\nAdditional Flags\n----------------\n\nThere are additional flags that might be useful for customizing tockloader's\noperation based on the requirements of a particular hardware platform.\n\n- `--app-address`: Manually specify the address at the beginning of where apps\n  are stored. This can be in hex or decimal.\n- `--bundle-apps`: This forces tockloader to write all apps as a concatenated\n  bundle using only a single flash command. This will require that anytime any\n  app changes in any way (e.g. its header changes or the app is updated or a new\n  app is installed) all apps are re-written.\n- `--layout`: Specify exactly how apps and padding apps should be written to the\n  board. This implies `--erase` and `--force` as all existing (even sticky) apps\n  will be removed.\n\n    The layout is specified as a string of how apps from TBFs and padding apps\n    should be written to the board. The syntax for the layout uses the following\n    identifiers:\n\n    - `T`: indicates to install a TBF app.\n    - `p\u003csize\u003e`: indicates to install a padding app of `\u003csize\u003e` bytes.\n\n    For example `--layout Tp1024TT` specifies to install the first app at the\n    `app-address`, then install a 1024 byte padding app, then install the second\n    app, then install the third app. No board-specific alignment or sizing will\n    be used; the apps will be installed exactly as described. It can be helpful\n    to use `tockloader list --map` to view how the apps were actually installed.\n\nCredentials and Integrity Support\n---------------------------------\n\nTockloader supports working with credentials stored in the TBF footer.\nTockloader will attempt to verify that stored credentials are valid for the\ngiven TBF. For credentials that require keys to verify, Tockloader can check the\ncredential using:\n\n    $ tockloader inspect-tab --verify-credentials [list of key files]\n    example:\n    $ tockloader inspect-tab --verify-credentials tockkey.public.der\n\nTockloader can also add credentials. To add a hash:\n\n    $ tockloader tbf credential add sha256\n\nTo add an RSA signature:\n\n    $ tockloader tbf credential add rsa2048 --private-key tockkey2048.private.der --public-key tockkey2048.public.der\n\nTo remove credentials:\n\n    $ tockloader tbf credential delete sha256\n\n\nFeatures\n--------\n\n- Supported communication protocols\n  - Serial over USB\n  - Segger JLinkExe JTAG support\n  - OpenOCD JTAG support\n- JLink RTT listener\n- JSON output using `--output-format json` for certain commands.\n\n\nComplete Install Instructions\n-----------------------------\n\nTockloader is a Python script that is installed as an executable.\nTo use Tockloader, you need python3, a couple dependencies, and\nthe Tockloader package.\n\n- Ubuntu\n    ```\n    sudo apt install python3-pip\n    pip3 install -U pip --user     # update pip\n    pip3 install tockloader --user\n    ```\n\n- MacOS\n    ```\n    brew install python3\n    pip3 install tockloader\n    ```\n\n- Windows\n    - [Download and Install Python 3](https://www.python.org/downloads/windows/)\n    - Execute within CMD/PowerShell/...:\n        ```\n        pip3 install tockloader\n        ```\n\nInternal Notes\n--------------\n\n### Test Locally\n\nTo test the code locally without installing as a package, from the top-level\ndirectory:\n\n    python3 -m tockloader.main \u003cCOMMANDS\u003e\n\n\n### Build and Install Locally\n\n    pipx install build\n    pyproject-build\n    pipx install .\n\n### Upload to PyPI\n\n    pipx install build\n    pipx install flit\n    pyproject-build\n    flit publish\n\n\n### Build Docs\n\n    pip3 install mkdocs\n    cd docs\n    ./generate_docs.py\n    cd ..\n    mkdocs serve --dev-addr=0.0.0.0:8001\n\n### Create requirements.txt\n\n    pip3 install pipreqs\n    pipreqs . --force\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftock%2Ftockloader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftock%2Ftockloader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftock%2Ftockloader/lists"}