mirror of
https://github.com/PurpleI2P/pyseeder
synced 2025-09-12 13:52:00 +00:00
initialize repository
This commit is contained in:
commit
03af5634c1
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
venv/
|
||||||
|
transports.ini
|
70
README.md
Normal file
70
README.md
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
pyseeder
|
||||||
|
========
|
||||||
|
|
||||||
|
Reseed data managment tools for I2P
|
||||||
|
|
||||||
|
* Generate reseed signing keypair
|
||||||
|
* Make reseed data files (su3)
|
||||||
|
* Download su3 files from official servers for mirroring
|
||||||
|
* Upload reseed data to different places (with plugins)
|
||||||
|
|
||||||
|
Reseed transports are implemented so that users can bootstrap their I2P nodes
|
||||||
|
without needing to connect to "official" I2P reseeds. This makes I2P more
|
||||||
|
invisible for firewalls.
|
||||||
|
|
||||||
|
|
||||||
|
Install requirements
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
# apt-get install python3 python3-pip
|
||||||
|
# pip3 install -r requirements.txt
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
$ python3 pyseeder.py --help
|
||||||
|
$ python3 pyseeder.py keygen --help
|
||||||
|
|
||||||
|
|
||||||
|
Generating keypair
|
||||||
|
------------------
|
||||||
|
|
||||||
|
|
||||||
|
$ pyhon3 pyseeder.py keygen --cert data/user_at_mail.i2p.crt --private-key data/priv_key.pem --signer-id user@mail.i2p
|
||||||
|
|
||||||
|
This will generate certificate (user\_at\_mail.i2p.crt) and private RSA key
|
||||||
|
(priv\_key.pem) in data folder. E-mail is used as certificate identifier.
|
||||||
|
|
||||||
|
Script will prompt for private key password.
|
||||||
|
|
||||||
|
|
||||||
|
Generating reseed data
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
|
||||||
|
$ YOUR_PASSWORD="Pa55w0rd"
|
||||||
|
$ echo $YOUR_PASSWORD | python3 pyseeder.py reseed --netdb /path/to/netDb --private-key data/priv_key.pem --outfile output/i2pseeds.su3 --signer-id user@mail.i2p
|
||||||
|
|
||||||
|
This will generate file i2pseeds.su3 in output folder, using user@mail.i2p as
|
||||||
|
certificate identifier.
|
||||||
|
|
||||||
|
Note: you'll have to enter your private key password to stdin, the above
|
||||||
|
is one of the ways to do it (for cron and scripts).
|
||||||
|
|
||||||
|
|
||||||
|
Download su3 file from official servers
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
$ python3 pyseeder.py transport.pull --urls https://reseed.i2p-projekt.de/ https://reseed.i2p.vzaws.com:8443/ --outfile output/i2pseeds.su3
|
||||||
|
|
||||||
|
Note: --urls parameter is optional, defaults are "official" I2P reseeds.
|
||||||
|
|
||||||
|
|
||||||
|
Upload su3 file with pluggable transports
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
$ python3 pyseeder.py transport.push --config transports.ini --file output/i2pseeds.su3
|
||||||
|
|
||||||
|
All parameters are optional. Copy file transports.ini.example to
|
||||||
|
transports.ini. Edit your settings in this new file.
|
2
data/.gitignore
vendored
Normal file
2
data/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*.crt
|
||||||
|
*.pem
|
1
output/.gitignore
vendored
Normal file
1
output/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.su3
|
125
pyseeder.py
Executable file
125
pyseeder.py
Executable file
@ -0,0 +1,125 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
import os, os.path
|
||||||
|
import sys
|
||||||
|
from getpass import getpass
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
from pyseeder.crypto import keygen
|
||||||
|
from pyseeder.su3file import SU3File
|
||||||
|
import pyseeder.transport
|
||||||
|
|
||||||
|
from pyseeder.utils import PyseederException
|
||||||
|
|
||||||
|
def keygen_action(args):
|
||||||
|
"""Sub-command to generate keys"""
|
||||||
|
priv_key_password = getpass("Set private key password: ").encode("utf-8")
|
||||||
|
keygen(args.cert, args.private_key, priv_key_password, args.signer_id)
|
||||||
|
|
||||||
|
def reseed_action(args):
|
||||||
|
"""Sub-command to generate reseed file"""
|
||||||
|
priv_key_password = input().encode("utf-8")
|
||||||
|
su3file = SU3File(args.signer_id)
|
||||||
|
su3file.reseed(args.netdb)
|
||||||
|
su3file.write(args.outfile, args.private_key, priv_key_password)
|
||||||
|
|
||||||
|
def transport_pull_action(args):
|
||||||
|
"""Sub-command for downloading su3 file"""
|
||||||
|
import random
|
||||||
|
random.shuffle(args.urls)
|
||||||
|
|
||||||
|
for u in args.urls:
|
||||||
|
if pyseeder.transport.download(u, args.outfile):
|
||||||
|
return True
|
||||||
|
|
||||||
|
raise PyseederException("Failed to download su3 file")
|
||||||
|
|
||||||
|
def transport_push_action(args):
|
||||||
|
"""Sub-command for uploading su3 file with transports"""
|
||||||
|
if not os.path.isfile(args.config):
|
||||||
|
raise PyseederException("Can't read transports config file")
|
||||||
|
|
||||||
|
import configparser
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(args.config)
|
||||||
|
pyseeder.transport.upload(args.file, config)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
subparsers = parser.add_subparsers(title="actions",
|
||||||
|
help="Command to execute")
|
||||||
|
|
||||||
|
kg_parser = subparsers.add_parser(
|
||||||
|
"keygen",
|
||||||
|
description="Generates keypair for your reseed",
|
||||||
|
usage="""
|
||||||
|
%(prog)s --cert data/user_at_mail.i2p.crt \\
|
||||||
|
--private-key data/priv_key.pem --signer-id user@mail.i2p"""
|
||||||
|
)
|
||||||
|
kg_parser.add_argument("--signer-id", required=True,
|
||||||
|
help="Identifier of certificate (example: user@mail.i2p)")
|
||||||
|
kg_parser.add_argument("--private-key", default="data/priv_key.pem",
|
||||||
|
help="RSA private key (default: data/priv_key.pem)")
|
||||||
|
kg_parser.add_argument("--cert", required=True,
|
||||||
|
help="Certificate (example: output/user_at_mail.i2p.crt)")
|
||||||
|
kg_parser.set_defaults(func=keygen_action)
|
||||||
|
|
||||||
|
|
||||||
|
rs_parser = subparsers.add_parser(
|
||||||
|
"reseed",
|
||||||
|
description="Creates su3 reseed file",
|
||||||
|
usage="""
|
||||||
|
echo $YOUR_PASSWORD | %(prog)s --netdb /path/to/netDb \\
|
||||||
|
--private-key data/priv_key.pem --outfile output/i2pseeds.su3 \\
|
||||||
|
--signer-id user@mail.i2p"""
|
||||||
|
)
|
||||||
|
rs_parser.add_argument("--signer-id", required=True,
|
||||||
|
help="Identifier of certificate (example: user@mail.i2p)")
|
||||||
|
rs_parser.add_argument("--private-key", default="data/priv_key.pem",
|
||||||
|
help="RSA private key (default: data/priv_key.pem)")
|
||||||
|
rs_parser.add_argument("-o", "--outfile", default="output/i2pseeds.su3",
|
||||||
|
help="Output file (default: output/i2pseeds.su3)")
|
||||||
|
rs_parser.add_argument("--netdb", required=True,
|
||||||
|
help="Path to netDb folder (example: ~/.i2pd/netDb)")
|
||||||
|
rs_parser.set_defaults(func=reseed_action)
|
||||||
|
|
||||||
|
|
||||||
|
tpull_parser = subparsers.add_parser(
|
||||||
|
"transport.pull",
|
||||||
|
description="Download su3 file from random reseed server",
|
||||||
|
usage="""
|
||||||
|
%(prog)s --urls https://reseed.i2p-projekt.de/ \\
|
||||||
|
https://reseed.i2p.vzaws.com:8443/ \\
|
||||||
|
--outfile output/i2pseeds.su3"""
|
||||||
|
)
|
||||||
|
tpull_parser.add_argument("--urls", default=pyseeder.transport.RESEED_URLS,
|
||||||
|
nargs="*", help="""Reseed URLs separated by space, default are
|
||||||
|
mainline I2P (like https://reseed.i2p-projekt.de/)""")
|
||||||
|
tpull_parser.add_argument("-o", "--outfile", default="output/i2pseeds.su3",
|
||||||
|
help="Output file (default: output/i2pseeds.su3)")
|
||||||
|
tpull_parser.set_defaults(func=transport_pull_action)
|
||||||
|
|
||||||
|
|
||||||
|
tpush_parser = subparsers.add_parser(
|
||||||
|
"transport.push",
|
||||||
|
description="Upload su3 file with transports",
|
||||||
|
usage="%(prog)s --config transports.ini --file output/i2pseeds.su3"
|
||||||
|
)
|
||||||
|
tpush_parser.add_argument("--config", default="transports.ini",
|
||||||
|
help="Transports config file (default: transports.ini)")
|
||||||
|
tpush_parser.add_argument("-f", "--file", default="output/i2pseeds.su3",
|
||||||
|
help=".su3 file (default: output/i2pseeds.su3)")
|
||||||
|
tpush_parser.set_defaults(func=transport_push_action)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
if hasattr(args, "func"):
|
||||||
|
try:
|
||||||
|
args.func(args)
|
||||||
|
except PyseederException as pe:
|
||||||
|
print("Pyseeder error: {}".format(pe))
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
parser.print_help()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
0
pyseeder/__init__.py
Normal file
0
pyseeder/__init__.py
Normal file
82
pyseeder/crypto.py
Normal file
82
pyseeder/crypto.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import os, os.path
|
||||||
|
import random
|
||||||
|
import sys
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from pyseeder.utils import PyseederException
|
||||||
|
|
||||||
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
from cryptography.hazmat.primitives import serialization
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
|
from cryptography import x509
|
||||||
|
from cryptography.x509.oid import NameOID
|
||||||
|
from cryptography.hazmat.primitives import hashes
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import padding
|
||||||
|
|
||||||
|
def keygen(pub_key, priv_key, priv_key_password, user_id):
|
||||||
|
"""Generate new private key and certificate RSA_SHA512_4096"""
|
||||||
|
for f in [pub_key, priv_key]:
|
||||||
|
if not os.access(os.path.dirname(f) or ".", os.W_OK):
|
||||||
|
raise PyseederException("Can't write {}, access forbidden").format(f)
|
||||||
|
|
||||||
|
# Generate our key
|
||||||
|
key = rsa.generate_private_key(public_exponent=65537, key_size=4096,
|
||||||
|
backend=default_backend())
|
||||||
|
|
||||||
|
# Write our key to disk for safe keeping
|
||||||
|
with open(priv_key, "wb") as f:
|
||||||
|
f.write(key.private_bytes(
|
||||||
|
encoding=serialization.Encoding.PEM,
|
||||||
|
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||||
|
encryption_algorithm=serialization.BestAvailableEncryption(
|
||||||
|
priv_key_password),
|
||||||
|
))
|
||||||
|
|
||||||
|
# Various details about who we are. For a self-signed certificate the
|
||||||
|
# subject and issuer are always the same.
|
||||||
|
subject = issuer = x509.Name([
|
||||||
|
x509.NameAttribute(NameOID.COUNTRY_NAME, "XX"),
|
||||||
|
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "XX"),
|
||||||
|
x509.NameAttribute(NameOID.LOCALITY_NAME, "XX"),
|
||||||
|
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "I2P Anonymous Network"),
|
||||||
|
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "I2P"),
|
||||||
|
x509.NameAttribute(NameOID.COMMON_NAME, user_id),
|
||||||
|
])
|
||||||
|
|
||||||
|
cert = x509.CertificateBuilder() \
|
||||||
|
.subject_name(subject) \
|
||||||
|
.issuer_name(issuer) \
|
||||||
|
.public_key(key.public_key()) \
|
||||||
|
.not_valid_before(datetime.datetime.utcnow()) \
|
||||||
|
.not_valid_after(
|
||||||
|
datetime.datetime.utcnow() + datetime.timedelta(days=365*10)
|
||||||
|
) \
|
||||||
|
.serial_number(random.randrange(1000000000, 2000000000)) \
|
||||||
|
.add_extension(
|
||||||
|
x509.SubjectKeyIdentifier.from_public_key(key.public_key()),
|
||||||
|
critical=False,
|
||||||
|
).sign(key, hashes.SHA512(), default_backend())
|
||||||
|
|
||||||
|
with open(pub_key, "wb") as f:
|
||||||
|
f.write(cert.public_bytes(serialization.Encoding.PEM))
|
||||||
|
|
||||||
|
|
||||||
|
def append_signature(target_file, priv_key, priv_key_password):
|
||||||
|
"""Append signature to the end of file"""
|
||||||
|
if not os.path.exists(priv_key):
|
||||||
|
raise PyseederException("Wrong private key path")
|
||||||
|
|
||||||
|
if not os.access(priv_key, os.R_OK):
|
||||||
|
raise PyseederException("Can't read private key, access forbidden")
|
||||||
|
|
||||||
|
with open(target_file, "rb") as f:
|
||||||
|
contents = f.read()
|
||||||
|
|
||||||
|
with open(priv_key, "rb") as kf:
|
||||||
|
private_key = serialization.load_pem_private_key(
|
||||||
|
kf.read(), password=priv_key_password, backend=default_backend())
|
||||||
|
|
||||||
|
signature = private_key.sign(contents, padding.PKCS1v15(), hashes.SHA512())
|
||||||
|
|
||||||
|
with open(target_file, "ab") as f:
|
||||||
|
f.write(signature)
|
84
pyseeder/su3file.py
Normal file
84
pyseeder/su3file.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import os, os.path
|
||||||
|
import time, datetime
|
||||||
|
import random
|
||||||
|
import io
|
||||||
|
|
||||||
|
from zipfile import ZipFile, ZIP_DEFLATED
|
||||||
|
|
||||||
|
import pyseeder.crypto
|
||||||
|
from pyseeder.utils import PyseederException
|
||||||
|
|
||||||
|
|
||||||
|
class SU3File:
|
||||||
|
"""SU3 file format"""
|
||||||
|
|
||||||
|
def __init__(self, signer_id):
|
||||||
|
self.SIGNER_ID = signer_id
|
||||||
|
self.SIGNER_ID_LENGTH = len(self.SIGNER_ID)
|
||||||
|
self.SIGNATURE_TYPE = 0x0006
|
||||||
|
self.SIGNATURE_LENGTH = 512
|
||||||
|
self.VERSION_LENGTH = 0x10
|
||||||
|
self.FILE_TYPE = None
|
||||||
|
self.CONTENT_TYPE = None
|
||||||
|
self.CONTENT = None
|
||||||
|
self.CONTENT_LENGTH = None
|
||||||
|
self.VERSION = str(int(time.time())).encode("utf-8")
|
||||||
|
#self.keytype = "RSA_SHA512_4096"
|
||||||
|
|
||||||
|
def write(self, filename, priv_key, priv_key_password):
|
||||||
|
"""Write file to disc"""
|
||||||
|
if not os.access(os.path.dirname(filename) or ".", os.W_OK):
|
||||||
|
raise PyseederException("Can't write su3 file, access forbidden")
|
||||||
|
|
||||||
|
nullbyte = bytes([0])
|
||||||
|
with open(filename, "wb") as f:
|
||||||
|
f.write("I2Psu3".encode("utf-8"))
|
||||||
|
f.write(bytes([0,0]))
|
||||||
|
f.write(self.SIGNATURE_TYPE.to_bytes(2, "big"))
|
||||||
|
f.write(self.SIGNATURE_LENGTH.to_bytes(2, "big"))
|
||||||
|
f.write(nullbyte)
|
||||||
|
f.write(bytes([self.VERSION_LENGTH]))
|
||||||
|
f.write(nullbyte)
|
||||||
|
f.write(bytes([self.SIGNER_ID_LENGTH]))
|
||||||
|
f.write(self.CONTENT_LENGTH.to_bytes(8, "big"))
|
||||||
|
f.write(nullbyte)
|
||||||
|
f.write(bytes([self.FILE_TYPE]))
|
||||||
|
f.write(nullbyte)
|
||||||
|
f.write(bytes([self.CONTENT_TYPE]))
|
||||||
|
f.write(bytes([0 for _ in range(12)]))
|
||||||
|
f.write(self.VERSION + bytes(
|
||||||
|
[0 for _ in range(16 - len(self.VERSION))]))
|
||||||
|
f.write(self.SIGNER_ID.encode("utf-8"))
|
||||||
|
f.write(self.CONTENT)
|
||||||
|
|
||||||
|
pyseeder.crypto.append_signature(filename, priv_key, priv_key_password)
|
||||||
|
|
||||||
|
def reseed(self, netdb):
|
||||||
|
"""Compress netdb entries and set content"""
|
||||||
|
zip_file = io.BytesIO()
|
||||||
|
dat_files = []
|
||||||
|
|
||||||
|
if not os.path.exists(netdb):
|
||||||
|
raise PyseederException("Wrong netDb path")
|
||||||
|
|
||||||
|
if not os.access(netdb, os.R_OK):
|
||||||
|
raise PyseederException("Can't read netDb, access forbidden")
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(netdb):
|
||||||
|
for f in files:
|
||||||
|
if f.endswith(".dat"):
|
||||||
|
# TODO check modified time
|
||||||
|
# may be not older than 10h
|
||||||
|
dat_files.append(os.path.join(root, f))
|
||||||
|
|
||||||
|
if len(dat_files) < 100:
|
||||||
|
raise PyseederException("Can't get enough netDb entries. Wrong netDb path?")
|
||||||
|
|
||||||
|
with ZipFile(zip_file, "w", compression=ZIP_DEFLATED) as zf:
|
||||||
|
for f in random.sample(dat_files, 75):
|
||||||
|
zf.write(f, arcname=os.path.split(f)[1])
|
||||||
|
|
||||||
|
self.FILE_TYPE = 0x00
|
||||||
|
self.CONTENT_TYPE = 0x03
|
||||||
|
self.CONTENT = zip_file.getvalue()
|
||||||
|
self.CONTENT_LENGTH = len(self.CONTENT)
|
58
pyseeder/transport.py
Normal file
58
pyseeder/transport.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
"""Module for managing transport tasks"""
|
||||||
|
import urllib.request
|
||||||
|
from urllib.error import URLError
|
||||||
|
import os, os.path
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
from pyseeder.utils import PyseederException
|
||||||
|
|
||||||
|
|
||||||
|
RESEED_URLS = [
|
||||||
|
"https://reseed.i2p-projekt.de/",
|
||||||
|
"https://i2p.mooo.com/netDb/",
|
||||||
|
"https://netdb.i2p2.no/",
|
||||||
|
"https://us.reseed.i2p2.no:444/",
|
||||||
|
"https://uk.reseed.i2p2.no:444/",
|
||||||
|
"https://i2p.manas.ca:8443/",
|
||||||
|
"https://i2p-0.manas.ca:8443/",
|
||||||
|
"https://reseed.i2p.vzaws.com:8443/",
|
||||||
|
"https://user.mx24.eu/",
|
||||||
|
"https://download.xxlspeed.com/",
|
||||||
|
]
|
||||||
|
|
||||||
|
def download(url, filename):
|
||||||
|
"""Download .su3 file, return True on success"""
|
||||||
|
USER_AGENT = "Wget/1.11.4"
|
||||||
|
|
||||||
|
url = "{}i2pseeds.su3".format(url)
|
||||||
|
req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
|
||||||
|
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req) as resp:
|
||||||
|
with open(filename, 'wb') as f:
|
||||||
|
f.write(resp.read())
|
||||||
|
|
||||||
|
if os.stat(filename).st_size > 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except URLError as e:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def upload(filename, config):
|
||||||
|
"""Upload .su3 file with transports"""
|
||||||
|
if "transports" in config and "enabled" in config["transports"]:
|
||||||
|
for t in config["transports"]["enabled"].split():
|
||||||
|
if t in config:
|
||||||
|
tconf = config[t]
|
||||||
|
else:
|
||||||
|
tconf = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
importlib.import_module("pyseeder.transports.{}".format(t)) \
|
||||||
|
.run(filename, tconf)
|
||||||
|
except ImportError:
|
||||||
|
raise PyseederException(
|
||||||
|
"{} transport can't be loaded".format(t))
|
||||||
|
|
0
pyseeder/transports/__init__.py
Normal file
0
pyseeder/transports/__init__.py
Normal file
3
pyseeder/transports/dropbox.py
Normal file
3
pyseeder/transports/dropbox.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
def run(filename, config):
|
||||||
|
print("dummy dropbox plugin")
|
39
pyseeder/transports/git.py
Normal file
39
pyseeder/transports/git.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
"""Git transport plugin"""
|
||||||
|
import subprocess
|
||||||
|
import os, os.path
|
||||||
|
from shutil import copyfile
|
||||||
|
from pyseeder.utils import TransportException
|
||||||
|
|
||||||
|
TRANSPORT_NAME = "git"
|
||||||
|
|
||||||
|
# Push to github repo witout prompting password.
|
||||||
|
# Set up SSH keys or change origin URL like that:
|
||||||
|
# git remote set-url origin https://$USERNAME:$PASSWORD@github.com/$USERNAME/$REPO.git
|
||||||
|
|
||||||
|
def run(filename, config):
|
||||||
|
if "folder" not in config:
|
||||||
|
raise TransportException("git: No folder specified in config")
|
||||||
|
else:
|
||||||
|
REPO_FOLDER = config["folder"]
|
||||||
|
|
||||||
|
REPO_FILE = os.path.split(filename)[1]
|
||||||
|
|
||||||
|
if not os.access(REPO_FOLDER, os.W_OK):
|
||||||
|
raise TransportException("git: {} access forbidden" \
|
||||||
|
.format(REPO_FOLDER))
|
||||||
|
|
||||||
|
if not os.path.isfile(filename):
|
||||||
|
raise TransportException("git: input file not found")
|
||||||
|
|
||||||
|
copyfile(filename, os.path.join(REPO_FOLDER, REPO_FILE))
|
||||||
|
|
||||||
|
commands = [
|
||||||
|
"git add {}".format(REPO_FILE),
|
||||||
|
"git commit -m 'update'",
|
||||||
|
"git push origin master"
|
||||||
|
]
|
||||||
|
|
||||||
|
cwd = os.getcwd()
|
||||||
|
os.chdir(REPO_FOLDER)
|
||||||
|
for c in commands: subprocess.call(c, shell=True)
|
||||||
|
os.chdir(cwd)
|
7
pyseeder/utils.py
Normal file
7
pyseeder/utils.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
"""Various code"""
|
||||||
|
|
||||||
|
class PyseederException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TransportException(PyseederException):
|
||||||
|
pass
|
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
cffi==1.7.0
|
||||||
|
cryptography==1.4
|
||||||
|
idna==2.1
|
||||||
|
pyasn1==0.1.9
|
||||||
|
pycparser==2.14
|
||||||
|
six==1.10.0
|
10
transports.ini.example
Normal file
10
transports.ini.example
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[transports]
|
||||||
|
; enabled transports separated by space
|
||||||
|
enabled=git
|
||||||
|
|
||||||
|
[git]
|
||||||
|
; Folder with git repository to use
|
||||||
|
folder=/home/user/reseed-data-repo
|
||||||
|
|
||||||
|
[dropbox]
|
||||||
|
; todo
|
Loading…
x
Reference in New Issue
Block a user