blob: bb6a9294407b1b9bb8835b27bcef0eb7a5c029d2 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import tempfile
import textwrap
import unittest
from contextlib import contextmanager
from pathlib import Path
from unittest import mock
from publish import local_dev_dependency_names
from publish import load_manifest
from publish import publish_package
from publish import strip_local_dev_dependencies
def write(path: Path, content: str) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(textwrap.dedent(content).strip() + "\n", encoding="utf-8")
class ReleaseRustPublishTest(unittest.TestCase):
def test_strip_local_dev_dependencies(self):
with tempfile.TemporaryDirectory() as tmpdir:
root = Path(tmpdir)
package = root / "crate"
write(
package / "Cargo.toml",
"""
[package]
name = "crate"
version = "0.1.0"
[dependencies]
local-normal = { path = "../local-normal", version = "0.1.0" }
[dev-dependencies]
external-dev = "1"
local-dev = { path = "../local-dev", version = "0.1.0" }
local-dev-multiline = { path = "../local-dev-multiline", version = "0.1.0", features = [
"test",
] }
[target.'cfg(unix)'.dev-dependencies]
local-target-dev = { path = "../local-target-dev", version = "0.1.0" }
""",
)
for name in (
"local-normal",
"local-dev",
"local-dev-multiline",
"local-target-dev",
):
(root / name).mkdir()
manifest = load_manifest(package / "Cargo.toml")
names = local_dev_dependency_names(manifest, package)
self.assertEqual(
names,
{"local-dev", "local-dev-multiline", "local-target-dev"},
)
changed = strip_local_dev_dependencies(package / "Cargo.toml", names)
self.assertTrue(changed)
stripped = (package / "Cargo.toml").read_text()
self.assertIn("local-normal", stripped)
self.assertIn("external-dev", stripped)
self.assertNotIn("local-dev =", stripped)
self.assertNotIn("local-dev-multiline", stripped)
self.assertNotIn("local-target-dev", stripped)
def test_live_publish_fetches_a_new_token_for_every_attempt(self):
with tempfile.TemporaryDirectory() as tmpdir:
root = Path(tmpdir)
package = root / "crate"
write(
package / "Cargo.toml",
"""
[package]
name = "crate"
version = "0.1.0"
""",
)
tokens = iter(("first-token", "second-token"))
released: list[str] = []
cargo_tokens: list[str | None] = []
@contextmanager
def token_provider():
token = next(tokens)
try:
yield token
finally:
released.append(token)
results = iter(
(
subprocess_result(
1, "Too many requests. Please try again after invalid."
),
subprocess_result(0, "published"),
)
)
def run(*args, **kwargs):
cargo_tokens.append(kwargs["env"].get("CARGO_REGISTRY_TOKEN"))
return next(results)
with (
mock.patch.dict(
"publish.os.environ",
{"CARGO_REGISTRY_TOKEN": "legacy-token"},
clear=True,
),
mock.patch(
"publish.temporary_trusted_publishing_token", token_provider
),
mock.patch("publish.subprocess.run", run),
mock.patch("publish.time.sleep") as sleep,
):
publish_package(
root,
"crate",
dry_run=False,
)
self.assertEqual(cargo_tokens, ["first-token", "second-token"])
self.assertEqual(released, ["first-token", "second-token"])
sleep.assert_called_once_with(610)
def test_dry_run_does_not_request_a_trusted_publishing_token(self):
with tempfile.TemporaryDirectory() as tmpdir:
root = Path(tmpdir)
package = root / "crate"
write(
package / "Cargo.toml",
"""
[package]
name = "crate"
version = "0.1.0"
""",
)
with (
mock.patch.dict(
"publish.os.environ",
{"CARGO_REGISTRY_TOKEN": "legacy-token"},
clear=True,
),
mock.patch(
"publish.temporary_trusted_publishing_token"
) as token_provider,
mock.patch(
"publish.subprocess.run",
return_value=subprocess_result(0, "checked"),
) as run,
):
publish_package(root, "crate", dry_run=True)
token_provider.assert_not_called()
self.assertIn("--dry-run", run.call_args.args[0])
self.assertNotIn("CARGO_REGISTRY_TOKEN", run.call_args.kwargs["env"])
def subprocess_result(returncode: int, output: str):
return mock.Mock(returncode=returncode, stdout=output)
if __name__ == "__main__":
unittest.main()