|
14 | 14 | # limitations under the License. |
15 | 15 |
|
16 | 16 | import os |
| 17 | +import sys |
17 | 18 | import pathlib |
18 | 19 | import jinja2 |
19 | 20 | import platform |
20 | 21 | import subprocess |
| 22 | +import getpass |
| 23 | +import argparse |
21 | 24 |
|
22 | | -# Functions |
23 | | -# Custom filter for Jinja2 to determine the directory of a given file path |
24 | | -def dirname(path): |
25 | | - return os.path.dirname(path) |
26 | | - |
27 | | -# Set filepath and file variables |
28 | | -root_dir = pathlib.Path(__file__).absolute().parent.parent |
29 | | -pkg_dir = root_dir.joinpath("pfSense-pkg-API") |
30 | | -template_dir = root_dir.joinpath("tools").joinpath("templates") |
31 | | -files_dir = pkg_dir.joinpath("files") |
32 | | -file_paths = {"dir": [], "file": []} |
33 | | -excluded_files = ["pkg-deinstall.in", "pkg-install.in", "etc", "usr"] |
34 | | - |
35 | | -# Set Jijna2 environment and variables |
36 | | -j2_env = jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath=str(template_dir))) |
37 | | -j2_env.filters["dirname"] = dirname |
38 | | -plist_template = j2_env.get_template("pkg-plist.j2") |
39 | | -makefile_template = j2_env.get_template("Makefile.j2") |
40 | | - |
41 | | -# Loop through each of our files and directories and store them for Jinja2 to render |
42 | | -for root, dirs, files in os.walk(files_dir, topdown=True): |
43 | | - root = pathlib.Path(str(root).replace(str(files_dir), "")) |
44 | | - for dir in dirs: |
45 | | - if dir not in excluded_files: |
46 | | - file_paths["dir"].append(str(root.joinpath(dir))) |
47 | | - for file in files: |
48 | | - if file not in excluded_files: |
49 | | - file_paths["file"].append(str(root.joinpath(file))) |
50 | | - |
51 | | -# Generate pkg-plist file |
52 | | -with open(pkg_dir.joinpath("pkg-plist"), "w") as pw: |
53 | | - pw.write(plist_template.render(files=file_paths)) |
54 | | -# Generate Makefile file |
55 | | -with open(pkg_dir.joinpath("Makefile"), "w") as mw: |
56 | | - mw.write(makefile_template.render(files=file_paths).replace(" ", "\t")) |
57 | | - |
58 | | -# If we are running on FreeBSD, make package. Otherwise display warning that package was not compiled |
59 | | -if platform.system() == "FreeBSD": |
60 | | - s = subprocess.call(["/usr/bin/make", "package", "-C", pkg_dir, "DISABLE_VULNERABILITIES=yes"]) |
61 | | -else: |
62 | | - print("WARNING: System is not FreeBSD. Generated Makefile and pkg-plist but did not attempt to make package.") |
| 25 | +class MakePackage: |
| 26 | + def __init__(self): |
| 27 | + self.__start_argparse__() |
| 28 | + self.port_version = self.args.tag.split(".")[2] |
| 29 | + self.port_revision = ".".join(self.args.tag.split(".")[0:2]) |
| 30 | + |
| 31 | + # Run tasks for build mode |
| 32 | + if self.args.build: |
| 33 | + self.build_on_remote_host() |
| 34 | + else: |
| 35 | + self.generate_makefile() |
| 36 | + |
| 37 | + # Custom filter for Jinja2 to determine the directory of a given file path |
| 38 | + def dirname(self, path): |
| 39 | + return os.path.dirname(path) |
| 40 | + |
| 41 | + def generate_makefile(self): |
| 42 | + # Set filepath and file variables |
| 43 | + root_dir = pathlib.Path(__file__).absolute().parent.parent |
| 44 | + pkg_dir = root_dir.joinpath("pfSense-pkg-API") |
| 45 | + template_dir = root_dir.joinpath("tools").joinpath("templates") |
| 46 | + files_dir = pkg_dir.joinpath("files") |
| 47 | + file_paths = {"dir": [], "file": [], "port_version": self.port_version, "port_revision": self.port_revision} |
| 48 | + excluded_files = ["pkg-deinstall.in", "pkg-install.in", "etc", "usr"] |
| 49 | + |
| 50 | + # Set Jijna2 environment and variables |
| 51 | + j2_env = jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath=str(template_dir))) |
| 52 | + j2_env.filters["dirname"] = self.dirname |
| 53 | + plist_template = j2_env.get_template("pkg-plist.j2") |
| 54 | + makefile_template = j2_env.get_template("Makefile.j2") |
| 55 | + |
| 56 | + # Loop through each of our files and directories and store them for Jinja2 to render |
| 57 | + for root, dirs, files in os.walk(files_dir, topdown=True): |
| 58 | + root = pathlib.Path(str(root).replace(str(files_dir), "")) |
| 59 | + for dir in dirs: |
| 60 | + if dir not in excluded_files: |
| 61 | + file_paths["dir"].append(str(root.joinpath(dir))) |
| 62 | + for file in files: |
| 63 | + if file not in excluded_files: |
| 64 | + file_paths["file"].append(str(root.joinpath(file))) |
| 65 | + |
| 66 | + # Generate pkg-plist file |
| 67 | + with open(pkg_dir.joinpath("pkg-plist"), "w") as pw: |
| 68 | + pw.write(plist_template.render(files=file_paths)) |
| 69 | + # Generate Makefile file |
| 70 | + with open(pkg_dir.joinpath("Makefile"), "w") as mw: |
| 71 | + mw.write(makefile_template.render(files=file_paths).replace(" ", "\t")) |
| 72 | + |
| 73 | + self.build_package(pkg_dir) |
| 74 | + |
| 75 | + def run_ssh_cmd(self, cmd): |
| 76 | + ssh_cmd = "ssh {u}@{h} '{c}'".format(u=self.args.username, h=self.args.host, c=cmd) |
| 77 | + return subprocess.call(ssh_cmd, shell=True) |
| 78 | + |
| 79 | + def run_scp_cmd(self, src, dst, recurse=False): |
| 80 | + scp_cmd = "scp {r} {s} {d}".format(r="-r" if recurse else "", s=src, d=dst) |
| 81 | + return subprocess.call(scp_cmd, shell=True) |
| 82 | + |
| 83 | + def build_package(self, pkg_dir): |
| 84 | + # If we are running on FreeBSD, make package. Otherwise display warning that package was not compiled |
| 85 | + if platform.system() == "FreeBSD": |
| 86 | + s = subprocess.call(["/usr/bin/make", "package", "-C", pkg_dir, "DISABLE_VULNERABILITIES=yes"]) |
| 87 | + else: |
| 88 | + print("WARNING: System is not FreeBSD. Generated Makefile and pkg-plist but did not attempt to build pkg.") |
| 89 | + |
| 90 | + def build_on_remote_host(self): |
| 91 | + # Automate the process to pull, build and retrieve the package on a remote host |
| 92 | + build_cmds = [ |
| 93 | + "mkdir -p ~/build/", |
| 94 | + "rm -rf ~/build/pfsense-api", |
| 95 | + "git clone https://github.com/jaredhendrickson13/pfsense-api.git ~/build/pfsense-api/", |
| 96 | + "git -C ~/build/pfsense-api checkout " + self.args.branch, |
| 97 | + "python3 ~/build/pfsense-api/tools/make_package.py --tag {t}".format(t=self.args.tag) |
| 98 | + ] |
| 99 | + |
| 100 | + # Join our build commands into a single command to run via SSH |
| 101 | + self.run_ssh_cmd(" && ".join(build_cmds)) |
| 102 | + |
| 103 | + # Retrieve the built package |
| 104 | + if self.args.freebsd == 11: |
| 105 | + src = "{u}@{h}:~/build/pfsense-api/pfSense-pkg-API/pfSense-pkg-API/pfSense-pkg-API-{v}{r}.txz" |
| 106 | + src = cmd.format( |
| 107 | + u=self.args.username, |
| 108 | + h=self.args.host, |
| 109 | + v=self.port_version, |
| 110 | + r="_" + self.port_revision if self.port_revision != "0" else "" |
| 111 | + ) |
| 112 | + self.run_scp_cmd(src, ".") |
| 113 | + else: |
| 114 | + src = "{u}@{h}:~/build/pfsense-api/pfSense-pkg-API/pfSense-pkg-API/work/pkg/pfSense-pkg-API-{v}{r}.txz" |
| 115 | + src = src.format( |
| 116 | + u=self.args.username, |
| 117 | + h=self.args.host, |
| 118 | + v=self.port_version, |
| 119 | + r="_" + self.port_revision if self.port_revision != "0" else "" |
| 120 | + ) |
| 121 | + self.run_scp_cmd(src, ".") |
| 122 | + |
| 123 | + def __start_argparse__(self): |
| 124 | + # Custom port type for argparse |
| 125 | + def tag(value_string): |
| 126 | + value = str(value_string).split(".") |
| 127 | + valid = True |
| 128 | + |
| 129 | + # Require 3 items (M.m.p) |
| 130 | + if len(value) == 3: |
| 131 | + for i in value: |
| 132 | + # Value cannot be blank |
| 133 | + if i == "": |
| 134 | + valid = False |
| 135 | + else: |
| 136 | + valid = False |
| 137 | + |
| 138 | + # Return value if valid, otherwise throw error |
| 139 | + if valid: |
| 140 | + return value_string |
| 141 | + else: |
| 142 | + raise argparse.ArgumentTypeError("%s is not a semantic version tag" % value_string) |
| 143 | + |
| 144 | + parser = argparse.ArgumentParser( |
| 145 | + description="Build the pfSense API on FreeBSD" |
| 146 | + ) |
| 147 | + parser.add_argument( |
| 148 | + '--host', '-i', |
| 149 | + dest="host", |
| 150 | + type=str, |
| 151 | + required=True if "--remote" in sys.argv or "-r" in sys.argv else False, |
| 152 | + help="The host to connect to when using --build mode" |
| 153 | + ) |
| 154 | + parser.add_argument( |
| 155 | + '--freebsd', '-f', |
| 156 | + dest="freebsd", |
| 157 | + type=int, |
| 158 | + default=12, |
| 159 | + help="The version of FreeBSD running on the remote host" |
| 160 | + ) |
| 161 | + parser.add_argument( |
| 162 | + '--branch', '-b', |
| 163 | + dest="branch", |
| 164 | + type=str, |
| 165 | + default="master", |
| 166 | + help="The branch to build" |
| 167 | + ) |
| 168 | + parser.add_argument( |
| 169 | + '--username', '-u', |
| 170 | + dest="username", |
| 171 | + type=str, |
| 172 | + default=getpass.getuser(), |
| 173 | + help="The username to use with SSH." |
| 174 | + ) |
| 175 | + parser.add_argument( |
| 176 | + '--tag', '-t', |
| 177 | + dest="tag", |
| 178 | + type=tag, |
| 179 | + required=True, |
| 180 | + help="The version tag to use when building." |
| 181 | + ) |
| 182 | + parser.add_argument( |
| 183 | + '--remote', '-r', |
| 184 | + dest="build", |
| 185 | + action="store_true", |
| 186 | + required=False, |
| 187 | + help='Enable remote build mode' |
| 188 | + ) |
| 189 | + self.args = parser.parse_args() |
| 190 | + |
| 191 | + |
| 192 | +MakePackage() |
0 commit comments