diff --git a/LICENSE b/LICENSE index 42cb419..098d76f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,22 +1,30 @@ -MIT License +BSD 3-Clause License -Copyright (c) Dataplicity +Copyright (c) 2026, Wildfoundry Ltd +All rights reserved. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index d3637fa..cb4c640 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ dataplicity --help ``` brew tap wildfoundry/tap -brew install dataplicity-cli +brew trust --formula wildfoundry/tap/dataplicity-cli +brew install wildfoundry/tap/dataplicity-cli dataplicity --help ``` diff --git a/pyproject.toml b/pyproject.toml index 4563d67..a997f06 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ version = "0.1.4" description = "Dataplicity command line interface" readme = "README.md" requires-python = ">=3.9" -license = { text = "MIT" } +license = "BSD-3-Clause" authors = [ { name = "Dataplicity" } ] diff --git a/tests/test_cli_ux_snapshots.py b/tests/test_cli_ux_snapshots.py index e4e5990..9e1f69d 100644 --- a/tests/test_cli_ux_snapshots.py +++ b/tests/test_cli_ux_snapshots.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +import re import tempfile import unittest from pathlib import Path @@ -17,9 +18,18 @@ def setUp(self) -> None: def _invoke(self, args: list[str]): return self.runner.invoke(app, args) + @staticmethod + def _strip_ansi(text: str) -> str: + return re.sub(r"\x1b\[[0-9;]*m", "", text) + + def _assert_output_contains(self, result, expected_snippets: list[str]) -> None: + self.assertEqual(result.exit_code, 0, msg=result.output) + output = self._strip_ansi(result.output) + for snippet in expected_snippets: + self.assertIn(snippet, output) + def test_root_help_snapshot(self) -> None: result = self._invoke(["--help"]) - self.assertEqual(result.exit_code, 0, msg=result.output) expected_snippets = [ "Dataplicity CLI", "Examples:", @@ -37,24 +47,20 @@ def test_root_help_snapshot(self) -> None: "ls", "connect", ] - for snippet in expected_snippets: - self.assertIn(snippet, result.output) + self._assert_output_contains(result, expected_snippets) def test_devices_terminal_help_snapshot(self) -> None: result = self._invoke(["devices", "terminal", "--help"]) - self.assertEqual(result.exit_code, 0, msg=result.output) expected_snippets = [ "Open an interactive terminal session to a device.", "Examples:", "dataplicity devices terminal", "dataplicity devices terminal ", ] - for snippet in expected_snippets: - self.assertIn(snippet, result.output) + self._assert_output_contains(result, expected_snippets) def test_devices_run_help_snapshot(self) -> None: result = self._invoke(["devices", "run", "--help"]) - self.assertEqual(result.exit_code, 0, msg=result.output) expected_snippets = [ "Run a single command on a selected device and print output.", "dataplicity devices run --command", @@ -63,12 +69,10 @@ def test_devices_run_help_snapshot(self) -> None: "--connect-timeout", "--no-timeout", ] - for snippet in expected_snippets: - self.assertIn(snippet, result.output) + self._assert_output_contains(result, expected_snippets) def test_devices_ssh_help_snapshot(self) -> None: result = self._invoke(["devices", "ssh", "--help"]) - self.assertEqual(result.exit_code, 0, msg=result.output) expected_snippets = [ "Open SSH to a device using an automatic secure tunnel.", "keys from ssh-agent by default", @@ -78,47 +82,39 @@ def test_devices_ssh_help_snapshot(self) -> None: "--local-port", "--identity-file", ] - for snippet in expected_snippets: - self.assertIn(snippet, result.output) + self._assert_output_contains(result, expected_snippets) def test_devices_port_forward_help_snapshot(self) -> None: result = self._invoke(["devices", "port-forward", "--help"]) - self.assertEqual(result.exit_code, 0, msg=result.output) expected_snippets = [ "Forward a local port to a remote device port with live metrics.", "dataplicity devices port-forward --remote-port 22 --local-port 2022", "--remote-port", "--local-port", ] - for snippet in expected_snippets: - self.assertIn(snippet, result.output) + self._assert_output_contains(result, expected_snippets) def test_doctor_help_snapshot(self) -> None: result = self._invoke(["doctor", "--help"]) - self.assertEqual(result.exit_code, 0, msg=result.output) expected_snippets = [ "Run connectivity and auth diagnostics.", "Examples:", "dataplicity doctor", "dataplicity --json doctor", ] - for snippet in expected_snippets: - self.assertIn(snippet, result.output) + self._assert_output_contains(result, expected_snippets) def test_fleet_jobs_run_help_snapshot(self) -> None: result = self._invoke(["fleet-jobs", "run", "--help"]) - self.assertEqual(result.exit_code, 0, msg=result.output) expected_snippets = [ "Create and start a fleet job.", "Examples:", "dataplicity fleet-jobs run --data", ] - for snippet in expected_snippets: - self.assertIn(snippet, result.output) + self._assert_output_contains(result, expected_snippets) def test_logging_list_help_snapshot(self) -> None: result = self._invoke(["logging", "list", "--help"]) - self.assertEqual(result.exit_code, 0, msg=result.output) expected_snippets = [ "List raw log lines.", "--device", @@ -127,18 +123,15 @@ def test_logging_list_help_snapshot(self) -> None: "--since", "--all-scopes", ] - for snippet in expected_snippets: - self.assertIn(snippet, result.output) + self._assert_output_contains(result, expected_snippets) def test_logging_path_map_help_snapshot(self) -> None: result = self._invoke(["logging", "path-map", "--help"]) - self.assertEqual(result.exit_code, 0, msg=result.output) expected_snippets = [ "Show recommended path filters for scoped log queries.", "dataplicity logging path-map", ] - for snippet in expected_snippets: - self.assertIn(snippet, result.output) + self._assert_output_contains(result, expected_snippets) def test_version_flag_snapshot(self) -> None: result = self._invoke(["--version"])