_< HAMYAR TOP
REFACTOR REFACTOR
# 79
1405-04-26 07:34

فک کنم بلاخره چیزی که میخواستم شد

از 12 شب تا الان دارم زجرررررر میکشمممم پاره شدم تازه هنوزم نمیدونم شد چیزی که میخواستم یا نه ببین همه مدل تست کردم اخر برای اینکه هی پکیج هارو از نت دانلود نکنه کارگو سر سرعت اینترنتتتتت اسکریپتمو تغییر دادم که همه وابستگی ها و پکیج هارو بیاره بصورت وندور کنه و بعدش با 

build:
      context: .
      dockerfile: Dockerfile
      additional_contexts:
        - global-vendor=/home/root/rust/global-vendor

این مدل توی داکر کامپوز باید دسترسی بهش بدی بعد از این اسکریپت که ai نوشت میتونی استفاده کنید
 

import os
import subprocess
import tomllib
from pathlib import Path
import shutil

DUMMY_PROJECT_DIR = "/home/root/rust/cargo-vendor"
GLOBAL_VENDOR_DIR = "/home/root/rust/global-vendor"

print("Cleaning up old directories...")

if os.path.exists(DUMMY_PROJECT_DIR):
    shutil.rmtree(DUMMY_PROJECT_DIR)
    
if os.path.exists(GLOBAL_VENDOR_DIR):
    shutil.rmtree(GLOBAL_VENDOR_DIR)
    
os.makedirs(DUMMY_PROJECT_DIR, exist_ok=True)


def format_toml_value(value):
    if isinstance(value, str):
        return f'"{value}"'
    elif isinstance(value, dict):
        items = []
        for k, v in value.items():
            if isinstance(v, str):
                items.append(f'{k} = "{v}"')
            elif isinstance(v, list):
                formatted_list = "[" + ", ".join(f'"{x}"' if isinstance(x, str) else str(x).lower() for x in v) + "]"
                items.append(f'{k} = {formatted_list}')
            elif isinstance(v, bool):
                items.append(f'{k} = {"true" if v else "false"}')
            else:
                items.append(f'{k} = {v}')
        return "{ " + ", ".join(items) + " }"
    else:
        return str(value).lower()

def extract_dependencies(cargo_toml_path):
    try:
        with open(cargo_toml_path, 'rb') as f:
            parsed = tomllib.load(f)
        
        merged_deps = {}
        sections = ['dependencies', 'build-dependencies', 'dev-dependencies']
        for section in sections:
            section_deps = parsed.get(section, {})
            for k, v in section_deps.items():
                # نادیده گرفتن وابستگی‌های محلی که با path مشخص شده‌اند
                if isinstance(v, dict) and 'path' in v:
                    print(f"   ⏭️  Skipped local path dependency: {k}")
                    continue
                merged_deps[k] = v
                
        return merged_deps
    except Exception as e:
        print(f"❌ Error reading {cargo_toml_path}: {e}")
        return {}

def main():
    home = Path.home()
    rust_dir = home / "rust"
    
    if not rust_dir.exists():
        print(f"❌ Directory not found: {rust_dir}")
        return
    
    output_dir = rust_dir / "cargo-vendor" 
    output_dir.mkdir(exist_ok=True)
    
    all_deps = {}
    
    cargo_files = list(rust_dir.rglob("Cargo.toml"))
    cargo_files = [f for f in cargo_files if "cargo-vendor" not in str(f.parent) and "target" not in str(f)]
    
    print(f"🔍 Found {len(cargo_files)} Cargo.toml files\n")
    
    for cargo_file in cargo_files:
        project_name = cargo_file.parent.name
        print(f"📦 Processing: {project_name}")
        
        deps = extract_dependencies(cargo_file)
        for name, raw_value in deps.items():
            formatted_version = format_toml_value(raw_value)
            if name not in all_deps:
                all_deps[name] = formatted_version
            else:
                if formatted_version != all_deps[name]:
                    pass # نادیده گرفتن هشدار Conflict برای خلوت شدن خروجی
                    
    print("")
    
    cargo_toml_path = output_dir / "Cargo.toml"
    with open(cargo_toml_path, 'w', encoding='utf-8') as f:
        f.write('[package]\n')
        f.write('name = "cargo-vendor"\n')
        f.write('version = "0.1.0"\n')
        f.write('edition = "2021"\n\n')
        
        f.write('[dependencies]\n')
        for name, formatted_version in sorted(all_deps.items()):
            f.write(f'{name} = {formatted_version}\n')
    
    print(f"✅ Created: {cargo_toml_path}")
    print(f"📊 Total dependencies to vendor: {len(all_deps)}")
    
    src_dir = output_dir / "src"
    src_dir.mkdir(exist_ok=True)
    main_rs = src_dir / "main.rs"
    with open(main_rs, 'w', encoding='utf-8') as f:
        f.write('fn main() {\n    println!("✅ Done");\n}\n')
    
    global_vendor_path = rust_dir / "global-vendor"

    print(f"⏳ Building global vendor at {global_vendor_path} (This will take a while...)")
    
    try:
        subprocess.run(
            ["cargo", "vendor", str(global_vendor_path)], 
            cwd=output_dir, 
            check=True
        )
        print(f"\n✅ Global vendor created successfully at: {global_vendor_path}")
    except subprocess.CalledProcessError as e:
        print(f"\n❌ Error during cargo vendor: {e}")

if __name__ == "__main__":
    main()

من یه پوشه /rust ساختم و توی روتش هر پروژم یه فولدره و این فایل پایتونه هم توی روتش گذاشتم 
فقط باید کارگو و راست رو روی سرور داشته باشید و پایتونه رو اجرا کنید خودش میره یه فایل براتون میسازه و وندورش رو هم اوکی میکنه 
توی داکر کامپوز هر پروژه هم ک بالا نوشتم چی بذارید اما خب حالا داکرش چی میشه ؟
 

FROM rust:alpine
# نصب پیش‌نیازها
# نصب پیش‌نیازها و protoc
RUN apk add --no-cache musl-dev pkgconfig openssl-dev openssl-libs-static protobuf protobuf-dev protoc

# تنظیم مسیر protoc برای prost-build
ENV PROTOC=/usr/bin/protoc
# نصب cargo-chef
RUN cargo install cargo-chef
WORKDIR /app

WORKDIR /app

# ساخت اتوماتیک فایل کانفیگ برای ارجاع به پوشه وندور داخل داکر
RUN mkdir -p .cargo && \
    echo '[source.crates-io]' > .cargo/config.toml && \
    echo 'replace-with = "vendored-sources"' >> .cargo/config.toml && \
    echo '[source.vendored-sources]' >> .cargo/config.toml && \
    echo 'directory = "/vendor"' >> .cargo/config.toml
COPY . .

RUN --mount=type=bind,from=global-vendor,source=/,target=/vendor \
    --mount=type=cache,target=/app/target \
    cargo build --release --offline && \
    cp /app/target/release/appname_rust /appname_rust_bin
    FROM alpine:latest

# نصب فقط ca-certificates (همون چیزی که می‌خوای)
RUN apk add --no-cache ca-certificates \
    && rm -rf /var/cache/apk/*

# تنظیم وورک دایرکتوری پیش‌فرض
WORKDIR /app
COPY --from=builder /appname_rust_bin ./appname_rust
EXPOSE 1005
CMD ["./appname_rust"]
    

هنوز جرات نکردم داکر ایمیج بگیرم میترسم گوه زده باشه به حجم ولی هوش مصنوعی من رو اسفالت کرد سر این مخصوصا

من : ک...ک.... همین ک بود

اون : 😂 آفرین! خودشه!
خب دررررد بگیریی ساعت 5 صبح وسط باگ و نت خراب و جنگ و کوفت و صدای زوزه باد همون کد قبلی رو میگههه 

اینم توضیح ai از این کار 

ساخت یک Pipeline بهینه برای Build پروژه‌های Rust با Docker

 

برای پروژه‌های Rust یک Pipeline ساختم که هدفش کاهش زمان Build و مستقل شدن از اینترنت بود.

اول یک اسکریپت پایتون نوشتم که تمام فایل‌های Cargo.toml پروژه‌ها را اسکن می‌کند، وابستگی‌ها را از بخش‌های مختلف (dependencies، build و dev) جمع می‌کند و از همه آن‌ها یک Global Vendor می‌سازد. به این ترتیب فقط یک بار تمام crateها دانلود می‌شوند و همه پروژه‌ها از همان مخزن محلی استفاده می‌کنند.

در Dockerfile هم با استفاده از قابلیت‌های جدید BuildKit، پوشه Vendor به‌صورت Read-Only داخل کانتینر Mount می‌شود و cargo build --offline بدون نیاز به اینترنت اجرا می‌شود. علاوه بر آن، پوشه target به‌صورت Cache نگهداری می‌شود تا در Buildهای بعدی فقط فایل‌های تغییر کرده دوباره کامپایل شوند و سرعت Build چند برابر شود.

برای کاهش حجم خروجی هم از Multi-stage Build استفاده کردم؛ مرحله اول فقط مسئول کامپایل است و در مرحله دوم تنها فایل اجرایی به یک ایمیج Alpine منتقل می‌شود. نتیجه یک ایمیج Production بسیار سبک (حدود ۵ مگابایت) بدون Compiler، Cargo یا وابستگی‌های اضافه است.

در Docker Compose هم سرویس به‌گونه‌ای تنظیم شده که متغیرهای محیطی از فایل .env خوانده شوند، کانتینر در صورت خطا به‌صورت خودکار بالا بیاید، لاگ‌ها حجم مشخصی داشته باشند و از یک شبکه مشترک برای ارتباط با سایر سرویس‌ها استفاده کند.

در کنار این‌ها چند دستور مدیریتی هم برای پاک‌سازی Cacheهای Docker، بررسی مصرف دیسک و تولید کلیدهای رمزنگاری نگه داشتم تا نگهداری و استقرار پروژه ساده‌تر باشد.

 

نتیجه نهایی: Build سریع‌تر، مصرف اینترنت تقریباً صفر بعد از Vendor اولیه، خروجی بسیار سبک، Buildهای قابل تکرار (Reproducible)، و فرآیند Deploy مناسب برای محیط Production.