250 lines
7.9 KiB
Python
250 lines
7.9 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")
|
|
else:
|
|
passphrase = ""
|
|
|
|
|
|
@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)
|
|
|
|
|
|
@remote.command(name="list")
|
|
def lst():
|
|
"""
|
|
List all Remotes URLs
|
|
"""
|
|
config = cson.load(open(os.path.join(".sing", "system", "branchcf.cson")))
|
|
for name, url in config["remotes"].items():
|
|
print(f"{name} --> {url}")
|
|
|
|
|
|
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)
|
|
print("remote: Decompressing objects...")
|
|
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())
|
|
print("remote: Building Database...")
|
|
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"))
|
|
],
|
|
}
|
|
print("remote: Compressing Objects...")
|
|
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}]")
|