Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# cc-peer

> This is the final Claude-only release line. Its successor is
> [session-peer](https://github.com/abruption/session-peer), adding local and SSH
> Codex messaging. Install the new product explicitly with `pipx install session-peer`.
> Existing `cc-peer` commands remain available; `cc-peer update` does not install
> or rename the successor. Keep the old installation until migration is verified.

[![PyPI](https://img.shields.io/pypi/v/cc-peer)](https://pypi.org/project/cc-peer/)
[![CI](https://github.com/abruption/cc-peer/actions/workflows/ci.yml/badge.svg)](https://github.com/abruption/cc-peer/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
Expand Down
48 changes: 8 additions & 40 deletions cc_peer.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
import urllib.request
from pathlib import Path

__version__ = "0.5.0"
__version__ = "0.5.1"
GITHUB_REPO = "abruption/cc-peer"

# Claude Code refuses a same-machine message once its serialized form passes
Expand Down Expand Up @@ -741,47 +741,15 @@ def cmd_update(args: argparse.Namespace) -> int:
print(json.dumps(all_results, ensure_ascii=False))
return exit_code

tag, url = latest_release()
latest, current = parse_version(tag), parse_version(__version__)

if args.check:
state = "up to date" if current >= latest else f"{tag} available"
emit(
args.json,
{"current": __version__, "latest": tag, "outdated": current < latest},
f"cc-peer {__version__} — {state}",
)
return 0

if current >= latest:
emit(args.json, {"current": __version__, "latest": tag, "updated": False},
f"cc-peer {__version__} is already current ({tag}).")
return 0

target = Path(__file__).resolve()
try:
with urllib.request.urlopen(url, timeout=DETECT_TIMEOUT * 4) as response:
source = response.read()
except (urllib.error.URLError, OSError) as exc:
raise CcPeerError(f"could not download {tag}: {exc}") from exc
if b"__version__" not in source:
raise CcPeerError(f"what came back from {url} does not look like cc_peer.py")

# We are running from the file being replaced. Write beside it and rename,
# so a failed download can't leave a half-written script behind.
staged = target.with_suffix(".py.new")
try:
staged.write_bytes(source)
staged.chmod(target.stat().st_mode & 0o777)
staged.replace(target)
except OSError as exc:
staged.unlink(missing_ok=True)
raise CcPeerError(f"could not replace {target}: {exc}") from exc

# Frozen compatibility release: never install another product in place.
emit(
args.json,
{"current": __version__, "latest": tag, "updated": True, "path": str(target)},
f"cc-peer {__version__} → {tag} ({target})",
{"current": __version__, "latest": __version__, "outdated": False,
"updated": False, "retired": True, "successor": "session-peer",
"migrationUrl": "https://github.com/abruption/session-peer#moving-from-cc-peer"},
"cc-peer is retired at 0.5.1. Install session-peer separately: "
"pipx install session-peer. Existing Claude commands remain available. "
"See https://github.com/abruption/session-peer#moving-from-cc-peer",
)
return 0

Expand Down
18 changes: 18 additions & 0 deletions test_cc_peer.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,5 +446,23 @@ def test_update_host_check_reports_only(self):
push.assert_not_called()


class Retirement(unittest.TestCase):
def test_local_update_never_downloads_or_installs_successor(self):
import contextlib
import io
for flags in ([], ["--check"]):
output = io.StringIO()
with contextlib.redirect_stdout(output), \
mock.patch.object(cc_peer, "latest_release") as latest, \
mock.patch.object(cc_peer, "push_to_remote") as push:
self.assertEqual(cc_peer.main(["update", "--json", *flags]), 0)
result = json.loads(output.getvalue())
self.assertTrue(result["retired"])
self.assertFalse(result["updated"])
self.assertEqual(result["successor"], "session-peer")
latest.assert_not_called()
push.assert_not_called()


if __name__ == "__main__":
unittest.main()
Loading