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
75 changes: 75 additions & 0 deletions assets/sourceos/tests/test_cloudshell_fog_receipt_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""Validate TurtleTerm receipt context propagation for CloudShell FOG sessions."""

from __future__ import annotations

import json
import os
import subprocess
import sys
import tempfile
from pathlib import Path


REPO_ROOT = Path(__file__).resolve().parents[3]
TURTLE_WRAPPER = REPO_ROOT / "assets" / "sourceos" / "bin" / "turtle-term"


def read_ndjson(path: Path) -> list[dict]:
return [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line.strip()]


def main() -> int:
with tempfile.TemporaryDirectory() as tmp:
tmp_path = Path(tmp)
events = tmp_path / "events.ndjson"
receipts = tmp_path / "receipts"

env = dict(os.environ)
env.update(
{
"SOURCEOS_TERMINAL_SESSION_ID": "csf-session-0001",
"SOURCEOS_WORKSPACE": "workspace:lattice-demo",
"SOURCEOS_TERMINAL_EVENTS": str(events),
"SOURCEOS_TERMINAL_RECEIPTS": str(receipts),
"SOURCEOS_ACTOR_ID": "human:operator@example.com",
"SOURCEOS_POLICY_BUNDLE_ID": "policy:cloudshell-default",
"SOURCEOS_EXECUTION_DOMAIN": "cloudshell-fog/k8s",
}
)

result = subprocess.run(
[sys.executable, str(TURTLE_WRAPPER), "run", "--", sys.executable, "-c", "print('cloudshell-fog-ok')"],
cwd=str(REPO_ROOT),
env=env,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=False,
)

assert result.returncode == 0, result.stderr
assert "cloudshell-fog-ok" in result.stdout
assert events.exists(), "event stream missing"

rows = read_ndjson(events)
completed = [row for row in rows if row.get("event_type") == "command.completed"][-1]
receipt_path = Path(completed["receipt_path"])
assert receipt_path.exists(), f"receipt missing: {receipt_path}"

receipt = json.loads(receipt_path.read_text(encoding="utf-8"))
assert receipt["schema"] == "sourceos.terminal.receipt.v0"
assert receipt["session_id"] == "csf-session-0001"
assert receipt["workspace_id"] == "workspace:lattice-demo"
assert receipt["actor_id"] == "human:operator@example.com"
assert receipt["policy_bundle_id"] == "policy:cloudshell-default"
assert receipt["execution_domain"] == "cloudshell-fog/k8s"
assert receipt["stdout_digest"].startswith("sha256:")
assert receipt["stderr_digest"].startswith("sha256:")

print("validated CloudShell FOG receipt context propagation")
return 0


if __name__ == "__main__":
raise SystemExit(main())
12 changes: 12 additions & 0 deletions docs/integration/cloudshell-fog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# CloudShell FOG Integration Note

This document records the TurtleTerm-side integration point for CloudShell FOG.

TurtleTerm preserves SourceOS terminal receipt context.
CloudShell FOG provides session, placement, runtime, and audit context.

When both systems are used together, shared identifiers should allow receipts and session records to be correlated.

Tracked by:
- TurtleTerm issue #1
- SocioProphet/cloudshell-fog issue #35
2 changes: 1 addition & 1 deletion packaging/homebrew/Formula/turtle-term.rb
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def caveats
assert_match "TurtleTerm local agent gateway", shell_output("#{bin}/turtle-agentd --help")
assert_match "TurtleTerm agent gateway CLI", shell_output("#{bin}/turtle-agentctl --help")
if (bin/"turtle-agent-status").exist?
assert_match "TurtleTerm agent reliability status", shell_output("#{bin}/turtle-agent-status --help")
assert_match "SourceOS agent reliability artifacts", shell_output("#{bin}/turtle-agent-status --help")
end
assert_match "TurtleTerm tmux bridge", shell_output("#{bin}/turtle-tmux --help")

Expand Down
4 changes: 2 additions & 2 deletions packaging/scripts/build-rpm-package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ session ergonomics, and reproducible operator workflows.

%install
rm -rf %{buildroot}
TURTLE_TERM_STAGE_PREFIX=%{buildroot}/usr TURTLE_TERM_ETC_DIR=%{buildroot}/etc TURTLE_TERM_RUNTIME_PREFIX=/usr TURTLE_TERM_RUNTIME_ETC_DIR=/etc $repo_root/packaging/scripts/stage-linux-package.sh >/dev/null
TURTLE_TERM_STAGE_PREFIX=%{buildroot}/usr TURTLE_TERM_ETC_DIR=%{buildroot}/etc TURTLE_TERM_RUNTIME_PREFIX=/usr TURTLE_TERM_RUNTIME_ETC_DIR=/etc bash $repo_root/packaging/scripts/stage-linux-package.sh >/dev/null
cp $repo_root/LICENSE.md %{buildroot}/LICENSE.md
if [ -f $repo_root/THIRD_PARTY_NOTICES.md ]; then cp $repo_root/THIRD_PARTY_NOTICES.md %{buildroot}/THIRD_PARTY_NOTICES.md; fi

Expand Down Expand Up @@ -83,4 +83,4 @@ python3 "$repo_root/packaging/scripts/write-native-package-manifest.py" \
--arch "$arch" \
--out "$rpm.manifest.json"

echo "$rpm"
echo "$rpm"
4 changes: 2 additions & 2 deletions packaging/scripts/verify-linux-package-layout.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ EOF
done

prefix="$tmp/prefix"
TURTLE_TERM_STAGE_PREFIX="$prefix" "$repo_root/packaging/scripts/stage-linux-package.sh" >/dev/null
TURTLE_TERM_STAGE_PREFIX="$prefix" bash "$repo_root/packaging/scripts/stage-linux-package.sh" >/dev/null

required_paths=(
"$prefix/bin/turtleterm"
Expand Down Expand Up @@ -74,4 +74,4 @@ printf 'def hello():\n return "world"\n' > "$probe"
"$prefix/bin/turtle-language" diagnostics "$probe" >/dev/null
"$prefix/bin/turtle-language" symbols "$probe" >/dev/null

echo "verified TurtleTerm Linux package layout at $prefix"
echo "verified TurtleTerm Linux package layout at $prefix"
Loading