Skip to content

Commit e18861b

Browse files
authored
fix: Pin docutils<=0.21.2 to fix rst2ansi incompatibility (#3914)
Newer versions of `docutils` (>=0.22) are incompatible with the unmaintained `rst2ansi` library, causing a `ModuleNotFoundError` on startup when `rst2ansi` tries to import `docutils.utils.error_reporting`. Fixes #3912 Closes #3913
1 parent 3e05563 commit e18861b

9 files changed

Lines changed: 1414 additions & 885 deletions

File tree

.github/workflows/feature_test.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
- name: Set up uv
3131
uses: SFDO-Tooling/setup-uv@main
3232
with:
33-
version: "0.5.0"
33+
version: "0.8.4"
3434
enable-cache: true
3535
- name: Install dependencies
3636
run: uv sync --group docs
@@ -56,7 +56,7 @@ jobs:
5656
- name: Set up uv
5757
uses: SFDO-Tooling/setup-uv@main
5858
with:
59-
version: "0.5.0"
59+
version: "0.8.4"
6060
enable-cache: true
6161
- name: Install dependencies
6262
run: uv sync -p ${{ matrix.python-version }}
@@ -80,7 +80,7 @@ jobs:
8080
- name: Set up uv
8181
uses: SFDO-Tooling/setup-uv@main
8282
with:
83-
version: "0.5.0"
83+
version: "0.8.4"
8484
enable-cache: true
8585
- name: Install dependencies
8686
run: uv sync --all-extras -p ${{ matrix.python-version }}
@@ -99,7 +99,7 @@ jobs:
9999
- name: Set up uv
100100
uses: SFDO-Tooling/setup-uv@main
101101
with:
102-
version: "0.5.0"
102+
version: "0.8.4"
103103
enable-cache: true
104104
- name: Install dependencies
105105
run: uv sync -p 3.11

.github/workflows/release_test_sfdx.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848
- name: Set up uv
4949
uses: SFDO-Tooling/setup-uv@main
5050
with:
51-
version: "0.5.0"
51+
version: "0.8.4"
5252
enable-cache: true
5353
- name: Install Python dependencies
5454
run: uv sync

.github/workflows/slow_integration_tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
- name: Set up uv
3535
uses: SFDO-Tooling/setup-uv@main
3636
with:
37-
version: "0.5.0"
37+
version: "0.8.4"
3838
enable-cache: true
3939
- name: Install dependencies
4040
run: uv sync -p 3.11

cumulusci/cli/service.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,25 @@ def list_commands(self, ctx):
8383
services = self._get_services_config(runtime)
8484
return sorted(services.keys())
8585

86+
def invoke(self, ctx):
87+
"""Override to show available services instead of 'Missing command' error"""
88+
try:
89+
return super().invoke(ctx)
90+
except click.UsageError as e:
91+
if "Missing command" in str(e):
92+
# No subcommand provided - list available services
93+
services = self.list_commands(ctx)
94+
if services:
95+
click.echo("Available services:")
96+
for service in services:
97+
click.echo(f" {service}")
98+
else:
99+
click.echo("No services available to configure.")
100+
return
101+
else:
102+
# Re-raise other usage errors
103+
raise
104+
86105
def _build_param(self, attribute: str, details: dict) -> click.Option:
87106
required = details.get("required", False)
88107
default_factory: Optional[Callable] = self._get_callable_default(
@@ -267,6 +286,7 @@ def callback(*args, **kwargs):
267286
cls=ConnectServiceCommand,
268287
name="connect",
269288
help="Connect an external service to CumulusCI",
289+
no_args_is_help=False,
270290
)
271291
def service_connect():
272292
pass

cumulusci/cli/tests/test_service.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,10 @@ def test_service_connect__attr_with_default_value():
106106
# but input of an empty line accepts the default.
107107
assert "attr (example) [PRESET]: " in result.output
108108
service_config = runtime.keychain.get_service("test", "test-alias")
109-
with mock.patch(
110-
"cumulusci.core.config.base_config.STRICT_GETATTR", False
111-
), pytest.warns(DeprecationWarning, match="attr"):
109+
with (
110+
mock.patch("cumulusci.core.config.base_config.STRICT_GETATTR", False),
111+
pytest.warns(DeprecationWarning, match="attr"),
112+
):
112113
assert service_config.lookup("attr") == "PRESET"
113114
assert service_config.attr == "PRESET"
114115

@@ -132,9 +133,10 @@ def test_service_connect__attr_with_default_factory():
132133

133134
# The service should have the attribute value returned by the default factory.
134135
service_config = runtime.keychain.get_service("test", "test-alias")
135-
with mock.patch(
136-
"cumulusci.core.config.base_config.STRICT_GETATTR", False
137-
), pytest.warns(DeprecationWarning, match="attr"):
136+
with (
137+
mock.patch("cumulusci.core.config.base_config.STRICT_GETATTR", False),
138+
pytest.warns(DeprecationWarning, match="attr"),
139+
):
138140
assert service_config.lookup("attr") == "CALCULATED"
139141
assert service_config.attr == "CALCULATED"
140142

@@ -155,13 +157,14 @@ def test_service_connect__alias_already_exists():
155157
"test-type",
156158
"already-exists",
157159
runtime=runtime,
158-
input="new\ny\n",
160+
input="new\ny\nn\n",
159161
)
160162

161163
service_config = runtime.keychain.get_service("test-type", "already-exists")
162-
with mock.patch(
163-
"cumulusci.core.config.base_config.STRICT_GETATTR", False
164-
), pytest.warns(DeprecationWarning, match="attr"):
164+
with (
165+
mock.patch("cumulusci.core.config.base_config.STRICT_GETATTR", False),
166+
pytest.warns(DeprecationWarning, match="attr"),
167+
):
165168
assert service_config.lookup("attr") == "new"
166169
assert service_config.attr == "new"
167170

@@ -536,7 +539,7 @@ def test_service_connect__connected_app():
536539
"connect",
537540
"connected_app",
538541
"new",
539-
input="\n\nID\nSECRET\n",
542+
input="\n\nID\nSECRET\nn\n",
540543
runtime=runtime,
541544
)
542545

@@ -559,7 +562,7 @@ def test_service_connect__connected_app__with_cli_options():
559562
"new",
560563
"--login_url",
561564
"https://custom",
562-
input="\nID\nSECRET\n", # not prompted for login_url
565+
input="\nID\nSECRET\nn\n", # not prompted for login_url
563566
runtime=runtime,
564567
)
565568

cumulusci/cli/tests/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def run_click_command(cmd, *args, **kw):
1717

1818
def run_cli_command(*args, runtime=None, input=None, **kw):
1919
"""Run a click command with arg parsing and injected CCI runtime object."""
20-
runner = CliRunner(mix_stderr=False)
20+
runner = CliRunner()
2121
result = runner.invoke(
2222
cli,
2323
args,

cumulusci/tests/test_integration_infrastructure.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,15 @@ def _capture_orgid_using_task():
3535

3636
class TestIntegrationInfrastructure:
3737
"Test our two plugins for doing integration testing"
38+
3839
remembered_cli_specified_org_id = None
3940

4041
@pytest.mark.vcr()
4142
def test_prepare_for__test_integration_tests(
4243
self, create_task, run_code_without_recording
4344
):
4445
"Delete a VCR Cassette to ensure next test creates it."
46+
4547
# only delete the cassette if we can replace it
4648
def delete_cassette():
4749
if first_cassette.exists():
@@ -81,7 +83,7 @@ def setup():
8183

8284
run_code_without_recording(setup)
8385

84-
def test_file_was_not_created(self, capture_orgid_using_task):
86+
def test_file_was_not_created(self):
8587
filename = (
8688
Path(__file__).parent
8789
/ "cassettes/TestIntegrationInfrastructure.test_run_code_without_recording.yaml"

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ dependencies = [
5454
"simple-salesforce==1.11.4",
5555
"snowfakery>=4.0.0",
5656
"xmltodict",
57-
"docutils>=0.21.2",
57+
"docutils<=0.21.2",
5858
]
5959

6060
[dependency-groups]

0 commit comments

Comments
 (0)