Singularity/.sing/branches/foo/hooks/remote.py

235 lines
7.5 KiB
Python

import typer
import socket
import cson
import os
import sys
import pickle
from lib.db import Database
from lib.abc import FileWrap, Key
from lib.keyexchange import generate_keys
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
host = s.getsockname()[0]
s.close()
except:
host = "127.0.0.1"
remote = typer.Typer(name="remote")
cstep = 0
CHUNK_SIZE = 1
def chunks(lst, n):
"""Yield successive n-sized chunks from lst."""
for i in range(0, len(lst), n):
yield lst[i : i + n]
def cycle():
global cstep
steps = ["/", "-", "\\", "|"]
cstep += 1
if cstep == 4:
cstep = 0
return steps[cstep]
@remote.command()
def add(name: str, url: str):
"""
Add a remote named <name> for the repository at <url>. The command 'sing remote fetch' can then
be used to create and update remote branches (<name>/<branch>)
"""
config = cson.load(open(os.path.join(".sing", "system", "branchcf.cson")))
config["remotes"][name] = url
cson.dump(config, open(os.path.join(".sing", "system", "branchcf.cson"), "w+"))
@remote.command(name="keys")
def kg(
generate: bool = typer.Option(True, "-g", "--generate-new"),
publish: bool = typer.Option(False, "-p", "--publish"),
):
if generate:
generate_keys()
print("REMINDER : THIS KEY WILL BE USED TO ENCRYPT REMOTE PUSHES.")
print("Other Clients will not be able to decrypt the Push unless they")
print("receive the key. Use these commands to share your keys:")
print("\tsing remote keys --publish")
if publish:
if not os.path.exists(os.path.join(".sing", "system", ".keyfile")):
return print(
"Fatal -- no Keys found. Use the '-g' option to create new keys"
)
print("Importing Keys...")
key: Key = Key.kimport(os.path.join(".sing", "system", ".keyfile"))
print(key.randomart)
import getpass
print("Do you want to protect your Keys using a Passphrase?")
print("Recipients will be prompted for their Password before getting the Key")
p = input("[y/N]") or "n"
p = p.lower()
if p in ["y", "yes"]:
passphrase = getpass.getpass("Enter Passphrase : ")
pass_rep = getpass.getpass("Re-Type Passphrase:")
i = 1
while not pass_rep == passphrase and i < 3:
pass_rep = getpass.getpass(
"Wrong Passphrase - Please re-type Passphrase:"
)
i += 1
if i >= 3:
return print("Aborted - 3 Times entered Wrong Password")
@remote.command(name="get-url")
def gurl(remote_name):
"""
Retrieves the URLs for a Remote.
"""
config = cson.load(open(os.path.join(".sing", "system", "branchcf.cson")))
rmurl = config["remotes"].get(remote_name, "Fatal -- No Remote Found.")
print(rmurl)
def clone(url, init_fn, pull_fn):
"""
Download objects from remote repository
"""
import urllib.parse
parsed = urllib.parse.urlparse(url)
if os.path.exists(parsed.path.split("/")[-1]):
return print(f"Fatal -- File/Directory exists: {parsed.path.split('/')[-1]}")
print("Connecting to remote Server...")
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((socket.gethostbyname(parsed.hostname), 2991))
s = f"down {parsed.path}"
sock.send(f"{len(s)}".encode())
sock.recv(1024)
sock.send(f"down {parsed.path}".encode())
database = []
print("Initializing Repository...")
os.mkdir(parsed.path.split("/")[-1])
os.chdir(parsed.path.split("/")[-1])
while True:
print(f"remote: Receiving Objects... {cycle()}", end="\r")
sock.settimeout(1)
try:
packet = sock.recv(4096)
if not packet:
break
database.append(packet)
except Exception as e:
break
print("remote: Unpacking Objects...")
database = b"".join(database)
database = pickle.loads(database)
Main_DB = database.get("MainDB")
Branch_Config = database.get("Branches")
Commit_History = database.get("CommitHistory")
init_fn(".", Branch_Config["current_branch"], True)
os.chdir("..")
try:
config = cson.load(open(os.path.join(".sing", "system", "branchcf.cson")))
except:
config = {}
config = Branch_Config
cson.dump(config, open(os.path.join(".sing", "system", "branchcf.cson"), "w+"))
for i in Commit_History:
with open(os.path.join(".sing", "control", i.name), "wb+") as f:
f.write(i.data)
open(os.path.join(".sing", "system", "sing.db"), "wb+").write(Main_DB)
print(f"{url}/HEAD -> local/HEAD")
print("Pulling changes...")
pull_fn()
@remote.command()
def fetch(remote_name):
"""
Download objects from remote repository
"""
config = cson.load(open(os.path.join(".sing", "system", "branchcf.cson")))
lconfig = cson.load(open(os.path.join(".sing", "system", "localconfig.cson")))
rmurl = config["remotes"].get(remote_name)
print("Connecting to remote Server...")
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((socket.gethostbyname(rmurl), 2991))
s = f"down {lconfig['repo.name']}"
sock.send(f"{len(s)}".encode())
sock.recv(1024)
sock.send(f"down {lconfig['repo.name']}".encode())
database = []
while True:
print(f"remote: Receiving Objects... {cycle()}", end="\r")
sock.settimeout(1)
try:
packet = sock.recv(4096)
if not packet:
break
database.append(packet)
except Exception as e:
break
print("remote: Unpacking Objects...")
database = b"".join(database)
database = pickle.loads(database)
Main_DB = database.get("MainDB")
Branch_Config = database.get("Branches")
Commit_History = database.get("CommitHistory")
config = cson.load(open(os.path.join(".sing", "system", "branchcf.cson")))
config["branches"] = Branch_Config["branches"]
cson.dump(config, open(os.path.join(".sing", "system", "branchcf.cson"), "w+"))
for i in Commit_History:
with open(os.path.join(".sing", "control", i.name), "wb+") as f:
f.write(i.data)
open(os.path.join(".sing", "system", "sing.db"), "wb+").write(Main_DB)
print(f"{remote_name}/HEAD -> local/HEAD")
print("Use 'sing pull' to write into local files")
@remote.command()
def push(remote_name):
"""
Update remote refs along with associated objects
"""
config = cson.load(open(os.path.join(".sing", "system", "branchcf.cson")))
lconfig = cson.load(open(os.path.join(".sing", "system", "localconfig.cson")))
rmurl = config["remotes"].get(remote_name)
print("Connecting to remote Server...")
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((socket.gethostbyname(rmurl), 2991))
s = f"up {lconfig['repo.name']}"
sock.send(f"{len(s)}".encode())
sock.recv(8)
sock.send(s.encode())
db = {
"MainDB": open(os.path.join(".sing", "system", "sing.db"), "rb").read(),
"Branches": config,
"CommitHistory": [
FileWrap(i, open(os.path.join(".sing", "control", i), "rb").read())
for i in os.listdir(os.path.join(".sing", "control"))
],
}
db = pickle.dumps(db)
c = list(chunks(db, CHUNK_SIZE))
for i, chunk in enumerate(c):
print(f"Sending Objects... {i}/{len(c)-1} {cycle()}", end="\r")
sock.send(chunk)
print()
print(f"Origin -> HEAD[{remote_name}]")