wip
This commit is contained in:
parent
c786166eb0
commit
2c156c1c3f
13 changed files with 675 additions and 19 deletions
|
@ -23,6 +23,7 @@ def build(
|
||||||
)
|
)
|
||||||
|
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
|
print(result)
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
package_name = result.stdout.strip()
|
package_name = result.stdout.strip()
|
||||||
|
|
73
aides_spec/commands/checksums.py
Normal file
73
aides_spec/commands/checksums.py
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import typer
|
||||||
|
|
||||||
|
app = typer.Typer()
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_sha256(file_path):
|
||||||
|
sha256_hash = hashlib.sha256()
|
||||||
|
with open(file_path, "rb") as f:
|
||||||
|
for byte_block in iter(lambda: f.read(4096), b""):
|
||||||
|
sha256_hash.update(byte_block)
|
||||||
|
return sha256_hash.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def run_bash_command(command):
|
||||||
|
result = subprocess.run(
|
||||||
|
command,
|
||||||
|
shell=True,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise RuntimeError(f"Ошибка при выполнении команды: {result.stderr}")
|
||||||
|
return result.stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def download_file(url, dest_folder="downloads"):
|
||||||
|
os.makedirs(dest_folder, exist_ok=True)
|
||||||
|
local_filename = os.path.join(dest_folder, os.path.basename(url))
|
||||||
|
with requests.get(url, stream=True, timeout=3000) as r:
|
||||||
|
r.raise_for_status()
|
||||||
|
with open(local_filename, "wb") as f:
|
||||||
|
for chunk in r.iter_content(chunk_size=8192):
|
||||||
|
f.write(chunk)
|
||||||
|
return local_filename
|
||||||
|
|
||||||
|
|
||||||
|
def update_checksums(script_path):
|
||||||
|
# Выполняем bash-скрипт, чтобы получить массив sources
|
||||||
|
command = (
|
||||||
|
f"source {script_path} && printf '%s\\n' \"${{sources_amd64[@]}}\""
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
sources_output = run_bash_command(command)
|
||||||
|
except RuntimeError as e:
|
||||||
|
print(f"Ошибка выполнения скрипта: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
sources = sources_output.splitlines()
|
||||||
|
if not sources:
|
||||||
|
print("Не удалось получить массив sources_amd64.")
|
||||||
|
return
|
||||||
|
|
||||||
|
new_checksums = []
|
||||||
|
for source_url in sources:
|
||||||
|
print(f"Скачивание: {source_url}")
|
||||||
|
local_file = download_file(source_url)
|
||||||
|
checksum = calculate_sha256(local_file)
|
||||||
|
new_checksums.append(f"sha256:{checksum}")
|
||||||
|
os.remove(local_file)
|
||||||
|
|
||||||
|
new_checksums_block = " ".join(f"'{chk}'" for chk in new_checksums)
|
||||||
|
print(new_checksums_block)
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def checksums():
|
||||||
|
update_checksums(f"{os.getcwd()}/alr.sh")
|
|
@ -3,6 +3,7 @@ from typing import Annotated, Optional
|
||||||
|
|
||||||
import typer
|
import typer
|
||||||
|
|
||||||
|
from aides_spec.utils.empty_template import create_from_empty_template
|
||||||
from aides_spec.utils.from_pkgbuild import (
|
from aides_spec.utils.from_pkgbuild import (
|
||||||
create_from_pkgbuild,
|
create_from_pkgbuild,
|
||||||
download_pkgbuild,
|
download_pkgbuild,
|
||||||
|
@ -15,9 +16,13 @@ app = typer.Typer()
|
||||||
def create(
|
def create(
|
||||||
from_aur: Annotated[Optional[str], typer.Option()] = None,
|
from_aur: Annotated[Optional[str], typer.Option()] = None,
|
||||||
from_pkgbuild: Annotated[Optional[str], typer.Option()] = None,
|
from_pkgbuild: Annotated[Optional[str], typer.Option()] = None,
|
||||||
output_file: Annotated[str, typer.Option("--output", "-o")] = "alr.sh",
|
empty_template: Annotated[Optional[bool], typer.Option()] = None,
|
||||||
):
|
):
|
||||||
if from_aur:
|
output_file = "alr.sh"
|
||||||
|
|
||||||
|
if empty_template:
|
||||||
|
create_from_empty_template(output_file)
|
||||||
|
elif from_aur:
|
||||||
print(f"Загружаем PKGBUILD для пакета '{from_aur}' из AUR...")
|
print(f"Загружаем PKGBUILD для пакета '{from_aur}' из AUR...")
|
||||||
content = download_pkgbuild(from_aur)
|
content = download_pkgbuild(from_aur)
|
||||||
create_from_pkgbuild(content, output_file)
|
create_from_pkgbuild(content, output_file)
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import typer
|
import typer
|
||||||
|
|
||||||
from aides_spec.commands.build import app as build_app
|
from aides_spec.commands.build import app as build_app
|
||||||
|
from aides_spec.commands.checksums import app as checksums_app
|
||||||
from aides_spec.commands.create import app as create_app
|
from aides_spec.commands.create import app as create_app
|
||||||
|
|
||||||
app = typer.Typer()
|
app = typer.Typer()
|
||||||
|
|
||||||
app.add_typer(create_app)
|
app.add_typer(create_app)
|
||||||
app.add_typer(build_app)
|
app.add_typer(build_app)
|
||||||
|
app.add_typer(checksums_app)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
|
@ -1,10 +1,30 @@
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from typing_extensions import List, TypedDict
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from tree_sitter import Node, Tree
|
||||||
|
|
||||||
|
|
||||||
|
class Replaces(TypedDict):
|
||||||
|
node: Node
|
||||||
|
content: str
|
||||||
|
|
||||||
|
|
||||||
|
class Appends(TypedDict):
|
||||||
|
node: Node
|
||||||
|
content: str
|
||||||
|
|
||||||
|
|
||||||
class BaseReplacer:
|
class BaseReplacer:
|
||||||
def __init__(self, content, tree):
|
def __init__(self, content, tree: Tree, ctx: dict | None = None):
|
||||||
self.content = content
|
self.content = content
|
||||||
self.tree = tree
|
self.tree = tree
|
||||||
self.replaces = []
|
self.replaces: List[Replaces] = []
|
||||||
|
self.appends: List[Appends] = []
|
||||||
|
self.ctx = ctx
|
||||||
|
|
||||||
def _node_text(self, node):
|
def _node_text(self, node: Node):
|
||||||
"""Helper function to get the text of a node."""
|
"""Helper function to get the text of a node."""
|
||||||
return self.content[node.start_byte : node.end_byte].decode("utf-8")
|
return self.content[node.start_byte : node.end_byte].decode("utf-8")
|
||||||
|
|
||||||
|
@ -34,6 +54,28 @@ class BaseReplacer:
|
||||||
replace_info["node"].start_point[1] + len(replacement),
|
replace_info["node"].start_point[1] + len(replacement),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for append_info in sorted(
|
||||||
|
self.appends,
|
||||||
|
key=lambda x: x["node"].end_byte,
|
||||||
|
reverse=True,
|
||||||
|
):
|
||||||
|
insertion_point = append_info["node"].end_byte
|
||||||
|
append_content = append_info["content"].encode("utf-8")
|
||||||
|
new_content[insertion_point:insertion_point] = append_content
|
||||||
|
|
||||||
|
self.tree.edit(
|
||||||
|
start_byte=insertion_point,
|
||||||
|
old_end_byte=insertion_point,
|
||||||
|
new_end_byte=insertion_point + len(append_content),
|
||||||
|
start_point=append_info["node"].end_point,
|
||||||
|
old_end_point=append_info["node"].end_point,
|
||||||
|
new_end_point=(
|
||||||
|
append_info["node"].end_point[0],
|
||||||
|
append_info["node"].end_point[1] + len(append_content),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
return new_content
|
return new_content
|
||||||
|
|
||||||
def process(self):
|
def process(self):
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
from aides_spec.replacers.arch_replacer import ArchReplacer
|
||||||
|
from aides_spec.replacers.base import BaseReplacer
|
||||||
|
|
||||||
|
|
||||||
|
class ChecksumsReplacer(BaseReplacer):
|
||||||
|
CHECKSUMS_REPLACEMENTS = {
|
||||||
|
"b2sums": "blake2b-512",
|
||||||
|
"sha512sums": "sha512",
|
||||||
|
"sha384sums": "sha384",
|
||||||
|
"sha256sums": "sha256",
|
||||||
|
"sha224sums": "sha224",
|
||||||
|
"sha1sums": "sha1",
|
||||||
|
"md5sums": "md5",
|
||||||
|
}
|
||||||
|
|
||||||
|
def process(self):
|
||||||
|
root_node = self.tree.root_node
|
||||||
|
|
||||||
|
sums = self.CHECKSUMS_REPLACEMENTS.keys()
|
||||||
|
arches = ArchReplacer.ARCH_MAPPING.keys()
|
||||||
|
|
||||||
|
checksums = dict()
|
||||||
|
combinations = {(s, a) for s in sums for a in arches}.union(
|
||||||
|
{(s, None) for s in sums}
|
||||||
|
)
|
||||||
|
|
||||||
|
def find_replacements(node):
|
||||||
|
if node.type == "variable_assignment":
|
||||||
|
var_node = node.child_by_field_name("name")
|
||||||
|
value_node = node.child_by_field_name("value")
|
||||||
|
|
||||||
|
if var_node and value_node:
|
||||||
|
var_name = self._node_text(var_node)
|
||||||
|
for sum_part, arch_part in combinations:
|
||||||
|
if (
|
||||||
|
sum_part == var_name
|
||||||
|
if arch_part is None
|
||||||
|
else f"{sum_part}_{arch_part}" == var_name
|
||||||
|
):
|
||||||
|
checksums[(sum_part, arch_part)] = []
|
||||||
|
for item in value_node.children:
|
||||||
|
if item.type == "raw_string":
|
||||||
|
element_text = self._node_text(item)
|
||||||
|
if (
|
||||||
|
element_text.startswith("'")
|
||||||
|
and element_text.endswith("'")
|
||||||
|
) or (
|
||||||
|
element_text.startswith('"')
|
||||||
|
and element_text.endswith('"')
|
||||||
|
):
|
||||||
|
# quote_char = element_text[0]
|
||||||
|
hash = element_text[1:-1]
|
||||||
|
else:
|
||||||
|
hash = element_text
|
||||||
|
|
||||||
|
chcksm = self.CHECKSUMS_REPLACEMENTS[
|
||||||
|
sum_part
|
||||||
|
]
|
||||||
|
|
||||||
|
checksums[(sum_part, arch_part)].append(
|
||||||
|
f"{chcksm}:{hash}"
|
||||||
|
)
|
||||||
|
self.replaces.append({"node": node, "content": ""})
|
||||||
|
|
||||||
|
for child in node.children:
|
||||||
|
find_replacements(child)
|
||||||
|
|
||||||
|
find_replacements(root_node)
|
||||||
|
|
||||||
|
result = dict()
|
||||||
|
|
||||||
|
content = ""
|
||||||
|
|
||||||
|
for (sum_part, arch_part), hashes in checksums.items():
|
||||||
|
key = (
|
||||||
|
f"checksums_{ArchReplacer.ARCH_MAPPING[arch_part]}"
|
||||||
|
if arch_part
|
||||||
|
else "checksums"
|
||||||
|
)
|
||||||
|
result.setdefault(key, []).extend(hashes)
|
||||||
|
|
||||||
|
for key, value in result.items():
|
||||||
|
content += f"""{key}=(
|
||||||
|
'{"',\n '".join(value)}'
|
||||||
|
)"""
|
||||||
|
|
||||||
|
self.appends.append({"node": root_node, "content": content})
|
||||||
|
|
||||||
|
print(result)
|
||||||
|
|
||||||
|
return self._apply_replacements()
|
94
aides_spec/replacers/local_sources_replacer.py
Normal file
94
aides_spec/replacers/local_sources_replacer.py
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from aides_spec.replacers.base import BaseReplacer
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from tree_sitter import Node
|
||||||
|
|
||||||
|
|
||||||
|
class LocalSourcesReplacer(BaseReplacer):
|
||||||
|
def process(self):
|
||||||
|
root_node = self.tree.root_node
|
||||||
|
|
||||||
|
self.local_files = []
|
||||||
|
self.prepare_func_body = None
|
||||||
|
|
||||||
|
def find_replacements(node: Node):
|
||||||
|
if node.type == "function_definition":
|
||||||
|
func_name = self._node_text(node.child_by_field_name("name"))
|
||||||
|
|
||||||
|
if func_name == "prepare":
|
||||||
|
self.prepare_func_body = node.child_by_field_name("body")
|
||||||
|
|
||||||
|
if node.type == "variable_assignment":
|
||||||
|
var_node = node.child_by_field_name("name")
|
||||||
|
value_node = node.child_by_field_name("value")
|
||||||
|
|
||||||
|
if var_node and value_node:
|
||||||
|
var_name = self._node_text(var_node)
|
||||||
|
if var_name == "sources":
|
||||||
|
self._remove_local_files(value_node)
|
||||||
|
|
||||||
|
for child in node.children:
|
||||||
|
find_replacements(child)
|
||||||
|
|
||||||
|
find_replacements(root_node)
|
||||||
|
|
||||||
|
copy_commands = "\n ".join(
|
||||||
|
f'cp "${{scriptdir}}/{file}" "${{srcdir}}"'
|
||||||
|
for file in self.local_files
|
||||||
|
)
|
||||||
|
|
||||||
|
prepare_func_content = f"""
|
||||||
|
{copy_commands}
|
||||||
|
"""
|
||||||
|
|
||||||
|
print(self.local_files)
|
||||||
|
|
||||||
|
if self.prepare_func_body is not None:
|
||||||
|
text = self._node_text(self.prepare_func_body)
|
||||||
|
closing_brace_index = text.rfind("}")
|
||||||
|
text = (
|
||||||
|
text[:closing_brace_index]
|
||||||
|
+ prepare_func_content
|
||||||
|
+ text[closing_brace_index:]
|
||||||
|
)
|
||||||
|
self.replaces.append(
|
||||||
|
{
|
||||||
|
"node": self.prepare_func_body,
|
||||||
|
"content": text,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
text = self._node_text(root_node)
|
||||||
|
text = f"""prepare() {{
|
||||||
|
{prepare_func_content}}}
|
||||||
|
"""
|
||||||
|
self.appends.append({"node": root_node, "content": text})
|
||||||
|
|
||||||
|
return self._apply_replacements()
|
||||||
|
|
||||||
|
def _remove_local_files(self, source_node: Node):
|
||||||
|
updated_items = []
|
||||||
|
for item_node in source_node.children:
|
||||||
|
item_text = self._node_text(item_node)
|
||||||
|
|
||||||
|
if item_text == "(" or item_text == ")":
|
||||||
|
continue
|
||||||
|
|
||||||
|
if "://" in item_text:
|
||||||
|
updated_items.append(item_text)
|
||||||
|
else:
|
||||||
|
text = item_text
|
||||||
|
if item_node.type == "string":
|
||||||
|
text = self._node_text(item_node.child(1))
|
||||||
|
|
||||||
|
self.local_files.append(text)
|
||||||
|
|
||||||
|
new_content = "(\n " + " \n".join(updated_items) + "\n)"
|
||||||
|
self.replaces.append(
|
||||||
|
{
|
||||||
|
"node": source_node,
|
||||||
|
"content": new_content,
|
||||||
|
}
|
||||||
|
)
|
|
@ -6,9 +6,10 @@ class SimpleReplacer(BaseReplacer):
|
||||||
"pkgname": "name",
|
"pkgname": "name",
|
||||||
"pkgver": "version",
|
"pkgver": "version",
|
||||||
"pkgrel": "release",
|
"pkgrel": "release",
|
||||||
"pkgdesc": "description",
|
"pkgdesc": "desc",
|
||||||
"url": "homepage",
|
"url": "homepage",
|
||||||
"arch": "architectures",
|
"arch": "architectures",
|
||||||
|
"depends": "deps",
|
||||||
"optdepends": "opt_deps",
|
"optdepends": "opt_deps",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
202
aides_spec/replacers/sources.py
Normal file
202
aides_spec/replacers/sources.py
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
import re
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from tree_sitter import Node
|
||||||
|
|
||||||
|
from aides_spec.replacers.arch_replacer import ArchReplacer
|
||||||
|
from aides_spec.replacers.base import BaseReplacer
|
||||||
|
|
||||||
|
|
||||||
|
class StringValue(str):
|
||||||
|
def __init__(self, node: Node):
|
||||||
|
self.node = node
|
||||||
|
|
||||||
|
def get_text_value(self) -> str:
|
||||||
|
value = self.node.text.decode("utf-8")
|
||||||
|
return value[1:-1]
|
||||||
|
|
||||||
|
def get_quote(self) -> str:
|
||||||
|
value = self.node.text.decode("utf-8")
|
||||||
|
return value[0]
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return self.node.text.decode("utf-8")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.__repr__()
|
||||||
|
|
||||||
|
|
||||||
|
class Utils:
|
||||||
|
def parse_variable_assignment(node: Node):
|
||||||
|
var_node = node.child_by_field_name("name")
|
||||||
|
value_node = node.child_by_field_name("value")
|
||||||
|
|
||||||
|
if not (var_node and value_node):
|
||||||
|
return None
|
||||||
|
|
||||||
|
return (var_node, value_node)
|
||||||
|
|
||||||
|
def get_string_values_from_array(node: Node):
|
||||||
|
arr = []
|
||||||
|
for item in node.children:
|
||||||
|
if (
|
||||||
|
item.type == "string"
|
||||||
|
or item.type == "raw_string"
|
||||||
|
or item.type == "concatenation"
|
||||||
|
):
|
||||||
|
arr.append(StringValue(item))
|
||||||
|
|
||||||
|
return arr
|
||||||
|
|
||||||
|
|
||||||
|
CHECKSUMS_REPLACEMENTS = {
|
||||||
|
"b2sums": "blake2b-512",
|
||||||
|
"sha512sums": "sha512",
|
||||||
|
"sha384sums": "sha384",
|
||||||
|
"sha256sums": "sha256",
|
||||||
|
"sha224sums": "sha224",
|
||||||
|
"sha1sums": "sha1",
|
||||||
|
"md5sums": "md5",
|
||||||
|
}
|
||||||
|
|
||||||
|
SOURCE_PATTERN = r"^source(?:_(x86_64|i686|armv7h|aarch64))?$"
|
||||||
|
CHECKSUMS_PATTERN = (
|
||||||
|
r"^(b2sums|sha512sums|sha384sums|sha256sums|sha224sums|sha1sums|md5sums)"
|
||||||
|
r"(?:_(x86_64|i686|armv7h|aarch64))?$"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SourcesReplacer(BaseReplacer):
|
||||||
|
def process(self):
|
||||||
|
root_node = self.tree.root_node
|
||||||
|
|
||||||
|
self.local_files = []
|
||||||
|
self.prepare_func_body = None
|
||||||
|
|
||||||
|
self.nodes_to_remove = []
|
||||||
|
|
||||||
|
sources = dict()
|
||||||
|
checksums = dict()
|
||||||
|
|
||||||
|
def execute(node: Node):
|
||||||
|
if node.type == "function_definition":
|
||||||
|
func_name = self._node_text(node.child_by_field_name("name"))
|
||||||
|
if func_name != "prepare":
|
||||||
|
return
|
||||||
|
self.prepare_func_body = node
|
||||||
|
return
|
||||||
|
|
||||||
|
if node.type == "variable_assignment":
|
||||||
|
var_node = node.child_by_field_name("name")
|
||||||
|
value_node = node.child_by_field_name("value")
|
||||||
|
if not (var_node and value_node):
|
||||||
|
return
|
||||||
|
|
||||||
|
var_name = self._node_text(var_node)
|
||||||
|
|
||||||
|
re_match = re.match(SOURCE_PATTERN, var_name)
|
||||||
|
if re_match:
|
||||||
|
self.nodes_to_remove.append(node)
|
||||||
|
|
||||||
|
arch = re_match.group(1)
|
||||||
|
sources[arch if arch else "-"] = (
|
||||||
|
Utils.get_string_values_from_array(value_node)
|
||||||
|
)
|
||||||
|
|
||||||
|
re_match = re.match(CHECKSUMS_PATTERN, var_name)
|
||||||
|
if re_match:
|
||||||
|
self.nodes_to_remove.append(node)
|
||||||
|
|
||||||
|
checksum = CHECKSUMS_REPLACEMENTS[re_match.group(1)]
|
||||||
|
arch = re_match.group(2)
|
||||||
|
|
||||||
|
checksums[arch if arch else "-"] = [
|
||||||
|
f"'{checksum}:{v.get_text_value()}'"
|
||||||
|
for v in Utils.get_string_values_from_array(value_node)
|
||||||
|
]
|
||||||
|
|
||||||
|
def traverse(node: Node):
|
||||||
|
execute(node)
|
||||||
|
for child in node.children:
|
||||||
|
traverse(child)
|
||||||
|
|
||||||
|
traverse(root_node)
|
||||||
|
|
||||||
|
content = ""
|
||||||
|
|
||||||
|
for node in self.nodes_to_remove:
|
||||||
|
self.replaces.append({"node": node, "content": ""})
|
||||||
|
|
||||||
|
for arch, files in sources.items():
|
||||||
|
source_files = []
|
||||||
|
checksums_str = []
|
||||||
|
|
||||||
|
for i, file in enumerate(files):
|
||||||
|
file_name = file.get_text_value()
|
||||||
|
print(file_name)
|
||||||
|
if "://" in file_name:
|
||||||
|
source_files.append(file)
|
||||||
|
checksums_str.append(checksums[arch][i])
|
||||||
|
else:
|
||||||
|
self.local_files.append(file)
|
||||||
|
|
||||||
|
content += self.source_to_str(arch, source_files) + "\n"
|
||||||
|
content += self.checksums_to_str(arch, checksums_str) + "\n"
|
||||||
|
|
||||||
|
if len(self.local_files) > 0:
|
||||||
|
copy_commands = "\n ".join(
|
||||||
|
f'cp "${{scriptdir}}/{file.get_text_value()}" "${{srcdir}}"'
|
||||||
|
for file in self.local_files
|
||||||
|
)
|
||||||
|
|
||||||
|
prepare_func_content = f"""
|
||||||
|
{copy_commands}
|
||||||
|
"""
|
||||||
|
if self.prepare_func_body is not None:
|
||||||
|
text = self._node_text(self.prepare_func_body)
|
||||||
|
closing_brace_index = text.rfind("}")
|
||||||
|
text = (
|
||||||
|
text[:closing_brace_index]
|
||||||
|
+ prepare_func_content
|
||||||
|
+ text[closing_brace_index:]
|
||||||
|
)
|
||||||
|
self.replaces.append(
|
||||||
|
{
|
||||||
|
"node": self.prepare_func_body,
|
||||||
|
"content": text,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
text = self._node_text(root_node)
|
||||||
|
content += f"""
|
||||||
|
prepare() {{
|
||||||
|
{prepare_func_content}}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.appends.append(
|
||||||
|
{
|
||||||
|
"node": root_node,
|
||||||
|
"content": content,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return self._apply_replacements()
|
||||||
|
|
||||||
|
def source_to_str(self, arch, files):
|
||||||
|
return f"""
|
||||||
|
{f"sources_{ArchReplacer.ARCH_MAPPING[arch]}" if arch != '-' else "sources"}=(
|
||||||
|
{'\n '.join([s.__str__() for s in files])}
|
||||||
|
)"""
|
||||||
|
|
||||||
|
def checksums_to_str(self, arch, files):
|
||||||
|
var_name = (
|
||||||
|
f"checksums_{ArchReplacer.ARCH_MAPPING[arch]}"
|
||||||
|
if arch != "-"
|
||||||
|
else "checksums"
|
||||||
|
)
|
||||||
|
|
||||||
|
return f"""
|
||||||
|
{var_name}=(
|
||||||
|
{'\n '.join([s.__str__() for s in files])}
|
||||||
|
)"""
|
54
aides_spec/utils/empty_template.py
Normal file
54
aides_spec/utils/empty_template.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
file = """name=example
|
||||||
|
version=1.0.0
|
||||||
|
release=1
|
||||||
|
epoch=0
|
||||||
|
desc=Description
|
||||||
|
homepage=https://exemple.com
|
||||||
|
maintainer=
|
||||||
|
# or 'all' for noarch
|
||||||
|
architectures=('amd64')
|
||||||
|
licenses=('custom:proprietary')
|
||||||
|
provides=()
|
||||||
|
conflicts=()
|
||||||
|
deps=()
|
||||||
|
build_deps=()
|
||||||
|
opt_deps=()
|
||||||
|
auto_deps=0
|
||||||
|
auto_req=0
|
||||||
|
replaces=()
|
||||||
|
sources=()
|
||||||
|
checksums=()
|
||||||
|
backup=()
|
||||||
|
scripts=(
|
||||||
|
['preinstall']='preinstall.sh'
|
||||||
|
['postinstall']='postinstall.sh'
|
||||||
|
['preremove']='preremove.sh'
|
||||||
|
['postremove']='postremove.sh'
|
||||||
|
['preupgrade']='preupgrade.sh'
|
||||||
|
['postupgrade']='postupgrade.sh'
|
||||||
|
['pretrans']='pretrans.sh'
|
||||||
|
['posttrans']='posttrans.sh'
|
||||||
|
)
|
||||||
|
|
||||||
|
prepare() {
|
||||||
|
echo "PREPARE"
|
||||||
|
}
|
||||||
|
build() {
|
||||||
|
echo "BUILD"
|
||||||
|
}
|
||||||
|
package() {
|
||||||
|
echo "PACKAGE"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def create_from_empty_template(output_file):
|
||||||
|
try:
|
||||||
|
with open(output_file, "w") as f:
|
||||||
|
f.write(file)
|
||||||
|
print(f"Файл успешно записан в {output_file}.")
|
||||||
|
except IOError as e:
|
||||||
|
print(f"Ошибка при записи файла: {e}")
|
||||||
|
sys.exit(1)
|
|
@ -1,29 +1,65 @@
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
import requests
|
import git
|
||||||
import tree_sitter_bash as tsbash
|
import tree_sitter_bash as tsbash
|
||||||
from tree_sitter import Language, Parser
|
from tree_sitter import Language, Parser
|
||||||
|
|
||||||
from aides_spec.replacers.arch_replacer import ArchReplacer
|
from aides_spec.replacers.arch_replacer import ArchReplacer
|
||||||
|
|
||||||
|
# from aides_spec.replacers.checksums_replacer import ChecksumsReplacer
|
||||||
|
# from aides_spec.replacers.local_sources_replacer import LocalSourcesReplacer
|
||||||
from aides_spec.replacers.simple_replacer import SimpleReplacer
|
from aides_spec.replacers.simple_replacer import SimpleReplacer
|
||||||
from aides_spec.replacers.sources_replacer import SourcesReplacer
|
from aides_spec.replacers.sources import SourcesReplacer
|
||||||
|
|
||||||
parser_ts = None
|
parser_ts = None
|
||||||
|
|
||||||
|
|
||||||
def download_pkgbuild(pkgname):
|
def download_pkgbuild_and_all_files(pkgname):
|
||||||
aur_url = (
|
aur_url = f"https://aur.archlinux.org/{pkgname}.git"
|
||||||
f"https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h={pkgname}"
|
|
||||||
)
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||||
try:
|
try:
|
||||||
response = requests.get(aur_url, timeout=300)
|
print(f"Клонируем репозиторий для {pkgname}")
|
||||||
response.raise_for_status()
|
git.Repo.clone_from(aur_url, tmpdirname)
|
||||||
return response.content
|
|
||||||
except requests.RequestException as e:
|
print(f"Файлы для {pkgname} загружены в {tmpdirname}")
|
||||||
print(f"Ошибка загрузки PKGBUILD из AUR: {e}")
|
|
||||||
|
for root, dirs, files in os.walk(tmpdirname):
|
||||||
|
dirs[:] = [d for d in dirs if d not in [".git"]]
|
||||||
|
files = [
|
||||||
|
file
|
||||||
|
for file in files
|
||||||
|
if file not in ["PKGBUILD", ".SRCINFO"]
|
||||||
|
]
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
file_path = os.path.join(root, file)
|
||||||
|
|
||||||
|
relative_path = os.path.relpath(file_path, tmpdirname)
|
||||||
|
destination_path = os.path.join(os.getcwd(), relative_path)
|
||||||
|
|
||||||
|
os.makedirs(
|
||||||
|
os.path.dirname(destination_path), exist_ok=True
|
||||||
|
)
|
||||||
|
|
||||||
|
shutil.copy(file_path, destination_path)
|
||||||
|
|
||||||
|
with open(os.path.join(tmpdirname, "PKGBUILD"), "rb") as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Ошибка при скачивании репозитория: {e}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def download_pkgbuild(pkgname):
|
||||||
|
return download_pkgbuild_and_all_files(pkgname)
|
||||||
|
|
||||||
|
|
||||||
def process_file(content, tree, replacers):
|
def process_file(content, tree, replacers):
|
||||||
for replacer_class in replacers:
|
for replacer_class in replacers:
|
||||||
replacer = replacer_class(content, tree)
|
replacer = replacer_class(content, tree)
|
||||||
|
@ -32,6 +68,13 @@ def process_file(content, tree, replacers):
|
||||||
return content
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
HEADER = """#
|
||||||
|
# WARNING: Automatic converted from PKGBUILD and may contains errors
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def create_from_pkgbuild(content, output_file):
|
def create_from_pkgbuild(content, output_file):
|
||||||
global parser_ts
|
global parser_ts
|
||||||
BASH_LANGUAGE = Language(tsbash.language())
|
BASH_LANGUAGE = Language(tsbash.language())
|
||||||
|
@ -43,10 +86,14 @@ def create_from_pkgbuild(content, output_file):
|
||||||
SimpleReplacer,
|
SimpleReplacer,
|
||||||
ArchReplacer,
|
ArchReplacer,
|
||||||
SourcesReplacer,
|
SourcesReplacer,
|
||||||
|
# LocalSourcesReplacer,
|
||||||
|
# ChecksumsReplacer,
|
||||||
]
|
]
|
||||||
|
|
||||||
new_content = process_file(content, tree, replacers)
|
new_content = process_file(content, tree, replacers)
|
||||||
|
|
||||||
|
new_content = bytes(HEADER, encoding="utf-8") + new_content
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(output_file, "wb") as f:
|
with open(output_file, "wb") as f:
|
||||||
f.write(new_content)
|
f.write(new_content)
|
||||||
|
|
45
poetry.lock
generated
45
poetry.lock
generated
|
@ -272,6 +272,38 @@ mccabe = ">=0.7.0,<0.8.0"
|
||||||
pycodestyle = ">=2.12.0,<2.13.0"
|
pycodestyle = ">=2.12.0,<2.13.0"
|
||||||
pyflakes = ">=3.2.0,<3.3.0"
|
pyflakes = ">=3.2.0,<3.3.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gitdb"
|
||||||
|
version = "4.0.12"
|
||||||
|
description = "Git Object Database"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf"},
|
||||||
|
{file = "gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
smmap = ">=3.0.1,<6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gitpython"
|
||||||
|
version = "3.1.44"
|
||||||
|
description = "GitPython is a Python library used to interact with Git repositories"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110"},
|
||||||
|
{file = "gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
gitdb = ">=4.0.1,<5"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
doc = ["sphinx (>=7.1.2,<7.2)", "sphinx-autodoc-typehints", "sphinx_rtd_theme"]
|
||||||
|
test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "identify"
|
name = "identify"
|
||||||
version = "2.6.4"
|
version = "2.6.4"
|
||||||
|
@ -597,6 +629,17 @@ files = [
|
||||||
{file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"},
|
{file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smmap"
|
||||||
|
version = "5.0.2"
|
||||||
|
description = "A pure Python implementation of a sliding window memory map manager"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e"},
|
||||||
|
{file = "smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stevedore"
|
name = "stevedore"
|
||||||
version = "5.4.0"
|
version = "5.4.0"
|
||||||
|
@ -753,4 +796,4 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.12"
|
python-versions = "^3.12"
|
||||||
content-hash = "fdfdf68234bf3f97b5eb66fb3d38a5069c489004693376e4a379bd821b460068"
|
content-hash = "3a3d39a47a858b743b5b52bffc42b105a750c5b94d67fdf468a8204ba0903aa3"
|
||||||
|
|
|
@ -30,6 +30,7 @@ tree-sitter = "^0.23.2"
|
||||||
tree-sitter-bash = "^0.23.3"
|
tree-sitter-bash = "^0.23.3"
|
||||||
requests = "^2.32.3"
|
requests = "^2.32.3"
|
||||||
typer = "^0.15.1"
|
typer = "^0.15.1"
|
||||||
|
gitpython = "^3.1.44"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
black = "^24.10.0"
|
black = "^24.10.0"
|
||||||
|
|
Loading…
Reference in a new issue