مدیر بسته

بسته‌های سلام در مخزن‌های معمولی گیت نگهداری می‌شوند. با یک دستور بسته‌ای را اضافه می‌کنید، با نشانی‌اش وارد می‌کنید و بسته‌ی خودتان را با فرستادن یک تگ منتشر می‌کنید.

این صفحه درباره‌ی بسته‌هایی است که دیگران در مخزن خودشان نگه می‌دارند. هرچه در کتابخانه‌ی استاندارد هست همراه مترجم نصب می‌شود و به salam add نیازی ندارد. بخش زبانی ماجرا، یعنی بسته، فراخوانی و همگانی، در درس هجدهم آمده است.

استفاده از یک بسته

پروژه پوشه‌ای است با یک main.salam و یک salam.toml. دستور salam init پوشه‌ی فعلی را به پروژه تبدیل می‌کند و فایل‌های موجود را دست نمی‌زند:

$ mkdir app && cd app
$ salam init
Created project 'app':
  main.salam
  salam.toml
  .gitignore

Next steps:
  salam run

$ salam add github.com/demo/slug
added github.com/demo/slug v1.1.0

دستور salam add تازه‌ترین انتشار مخزن را پیدا می‌کند، در ~/.salam/pkg دانلود می‌کند و در دو فایل ثبتش می‌کند. salam.toml می‌گوید شما چه خواسته‌اید و salam.lock دقیقاً ثبت می‌کند چه گرفته‌اید، یعنی کامیت و یک چک‌سام؛ برای همین روی هر ماشین همان کد ساخته می‌شود.

[dependencies]
"github.com/demo/slug" = "v1.1.0"

بسته‌ای که از مخزن می‌آید با نشانی داخل گیومه وارد می‌شود. آخرین بخش نشانی نامی می‌شود که در کد به کار می‌برید؛ پس github.com/demo/slug با slug صدا زده می‌شود:

واردسازی "github.com/demo/slug"

تابع ریشه:
    سرچاپ slug.بساز("Hello, World!")
    سرچاپ slug.بساز با سقف("Packages in Salam are simple"، 14)
پایان
$ salam run
hello-world
packages-in-sa

اگر دو بسته نام یکسان پیدا کنند، به یکی‌شان نام دیگری بدهید: import s "github.com/demo/slug" و بعد s.Make(...). برای رسیدن به بسته‌ای در پوشه‌ی یک مخزن، نام پوشه را به نشانی اضافه کنید: import "github.com/owner/repo/sub".

دستورهایی که بیشتر به کار می‌آیند:

دستورکار
salam add github.com/owner/repoتازه‌ترین انتشار را اضافه می‌کند؛ با @v1.2.0 یک تگ مشخص انتخاب می‌شود
salam remove github.com/owner/repoبسته را از salam.toml و salam.lock برمی‌دارد؛ فایل دانلودشده در انبار می‌ماند
salam updateهمه‌ی بسته‌ها یا فقط بسته‌های نام‌برده‌شده را به تازه‌ترین انتشار با همان نسخه‌ی اصلی می‌برد
salam depsهرچه در salam.lock است دانلود و چک‌سام همه را بررسی می‌کند؛ بعد از کلون‌کردن پروژه اجرایش کنید
salam search wordsدر کتابخانه‌ی استاندارد و رجیستری جست‌وجو می‌کند
salam install github.com/owner/toolmain.salam یک برنامه را می‌سازد و در ~/.salam/bin می‌گذارد
salam uninstall toolبرنامه‌ی نصب‌شده را برمی‌دارد

فایل‌های salam.toml و salam.lock را همراه کدتان کامیت کنید و بسته‌های دانلودشده را نه. بعد از هر کلون تازه فقط salam deps لازم است:

$ salam deps
github.com/demo/slug v1.1.0
downloaded 1 package(s)

حذف یک بسته، importهای آن را پاک نمی‌کند و مترجم این را به شما می‌گوید:

$ salam remove github.com/demo/slug
removed github.com/demo/slug
$ salam run
error[E008]: package 'github.com/demo/slug' is not a dependency of this project; run: salam add github.com/demo/slug

نوشتن یک بسته

بسته یک مخزن گیت است با یک فایل مانیفست و یک فایل ورودی. فایل ورودی نام مخزن را می‌گیرد؛ مخزن github.com/demo/slug این شکل را دارد:

slug/
  salam.toml        the manifest
  slug.salam        the entry file: package slug
  slug_test.salam   tests, run by salam test
  README.md
  LICENSE

قاعده‌ی نام فایل‌ها کوتاه است. برنامه، یعنی چیزی که اجرا می‌کنید، main.salam دارد و هیچ فایل دیگری هرگز ورودی حساب نمی‌شود. کتابخانه، یعنی چیزی که پروژه‌های دیگر وارد می‌کنند، اصلاً main ندارد؛ ورودی‌اش <name>.salam است و بسته‌ای که در پوشه‌ای از مخزن است <folder>/<folder>.salam نام دارد؛ همان فایلی که import "github.com/owner/repo/sub" بار می‌کند. فایل‌های دیگر همان بسته کنار فایل ورودی می‌نشینند و به آن پیوند می‌خورند، همان‌طور که در درس هجدهم دیدید.

مانیفست چند خط بیشتر نیست:

[package]
name = "slug"
version = "1.1.0"
salam = ">=0.6.1"
kind = "lib"

[dependencies]
"github.com/owner/other" = "v1.0.0"
  • name نام برنامه یا کتابخانه است و version برای آدم‌هاست؛ نسخه‌ای که برای کاربران حساب می‌شود تگ گیت است.
  • salam قدیمی‌ترین مترجمی است که بسته با آن ساخته می‌شود. اگر بسته مترجم جدیدتری بخواهد، salam add قبولش نمی‌کند و می‌گوید salam upgrade را اجرا کنید.
  • kind = "lib" کتابخانه بودن را اعلام می‌کند؛ برنامه آن را نمی‌نویسد.
  • [dependencies] نیازهای بسته‌ی شما را با همان قالبی فهرست می‌کند که salam add می‌نویسد. بسته‌های بسته‌های شما از مانیفست خودشان پیدا می‌شوند و اگر دو بسته نسخه‌های متفاوتی از یک بسته بخواهند، نسخه‌ی تازه‌تر به کار می‌رود.

حالا کد. هرچه همگانی (pub) بنویسید چهره‌ی عمومی بسته و قولی به کاربران است؛ بقیه را در هر انتشار بعدی می‌توانید تغییر نام بدهید یا حذف کنید. اینجا Make و MakeMax همگانی‌اند و is_word نیست. دو خط @en و @fa به هر تابع همگانی یک نام در هر دو زبان می‌دهند تا برنامه‌های انگلیسی و فارسی هر دو صدایش بزنند:

{{example.package-slug}}

هنگام کار، بسته را با یک برنامه‌ی کوچک کنارش امتحان کنید. فراخوانی فایلی از همین پروژه را بار می‌کند، پس بدون هیچ دانلودی اجرا می‌شود:

{{example.package-slug-demo}}

آزمون‌ها در فایلی می‌روند که نامش به _test.salam ختم شود و تابع‌هایش test_something نام داشته باشند. دستور salam test همه‌ی این فایل‌های پوشه را اجرا می‌کند، هر کدام در پردازش جدا:

{{example.package-slug-test}}
$ salam test
=== RUN   test_make
--- PASS: test_make
=== RUN   test_make_max
--- PASS: test_make_max
ok   slug_test.salam: 2 tests passed
PASS: 1 test files

کاربران فقط نام‌های همگانی را می‌بینند. اگر در پروژه‌ای که به بسته وابسته است، تابع کمکی خصوصی را صدا بزنید (در فایل انگلیسی)، رد می‌شود:

error[E017]: 'is_word' is not exported by package 'slug' (mark it 'pub')

انتشار بسته

مرحله‌ی آپلود و دستوری مثل salam publish وجود ندارد. وقتی تگی در یک مخزن عمومی گیت باشد، بسته منتشر شده است، چون salam add همین را می‌خواند. روند کار:

  1. بسته را در مخزنی هم‌نام خودش بگذارید و فایل ورودی و salam.toml را در سطح بالای مخزن (یا با چیدمان پوشه‌ای بالا) قرار دهید.
  2. بررسی کنید: salam test موفق باشد و salam check slug.salam خطایی نشان ندهد.
  3. یک README.md بنویسید که طرز افزودن و وارد کردن بسته و یک مثال کوتاه را نشان بدهد و یک فایل LICENSE اضافه کنید. بدون مجوز، هیچ‌کس اجازه ندارد از کدتان استفاده کند.
  4. کامیت کنید، انتشار را با تگ vMAJOR.MINOR.PATCH برچسب بزنید و تگ را push کنید.
$ git tag v1.1.0
$ git push origin main v1.1.0

از این لحظه هر کسی می‌تواند salam add github.com/owner/slug را بزند. پیش از اعلام بسته، خودتان در یک پروژه‌ی خالی امتحانش کنید: دقیقاً همان مسیری است که کاربرانتان می‌روند و نبودِ فایل ورودی را همان لحظه نشان می‌دهد.

بسته‌های gitlab.com و codeberg.org هم کار می‌کنند و اگر گیت نصب باشد، هر میزبان دیگری هم. مخزن‌های خصوصی با اعتبارنامه‌هایی که گیت از قبل دارد کار می‌کنند.

پیدا شدن بسته

دستور salam search فایل registry/index.toml را در مخزن سلام می‌خواند؛ فهرستی که با pull request به‌روز می‌شود. ثبت در آن اختیاری است، چون salam add برای هر مخزن عمومی کار می‌کند، ولی کمک می‌کند دیگران بسته را با کلمه‌هایش پیدا کنند:

[[package]]
name = "github.com/owner/slug"
description = "Turn text into URL slugs"
tags = ["text", "url"]

یک pull request باز کنید که فقط یک ورودی مثل بالا به فایل اضافه کند. مخزن باید salam.toml و فایل ورودی داشته باشد و انتشارهایش با تگ vX.Y.Z باشند. بعد از ادغام، salam search slug نام و توضیح را چاپ می‌کند.

همان مخزن فایل registry/sums.txt را هم نگه می‌دارد: فهرستی بازبینی‌شده از چک‌سام‌ها، با یک خط module@version sum برای هر انتشار. دانلودی که با خط منتشرشده فرق کند رد می‌شود. داخل پروژه‌ای که از بسته‌ی شما استفاده می‌کند، salam deps sums خط‌هایی را چاپ می‌کند که در pull request پیشنهاد می‌دهید:

$ salam deps sums
github.com/demo/[email protected] sha256:f20515bfd08073aebe3d95568695089f8bc7264437e02bad6d87e8b094b9efde

نسخه‌ها

هر انتشار یک تگ گیت به شکل v1.2.3 است. معنای همیشگی را رعایت کنید: وصله (v1.2.4) خطا را رفع می‌کند، نسخه‌ی فرعی (v1.3.0) چیزی اضافه می‌کند بی‌آنکه کد قدیمی را بشکند، و نسخه‌ی اصلی (v2.0.0) ممکن است بشکند. وقتی کسی تگی را دانلود کرده، جابه‌جا کردن آن امن نیست، چون چک‌سام در salam.lock او دیگر نمی‌خواند. به‌جایش نسخه‌ی تازه‌ای منتشر کنید.

نسخه در salam.toml به چهار شکل نوشته می‌شود:

نوشتهمعنی
"v1.2.0"دقیقاً همان تگ
"^1.2"هر 1.x از 1.2.0 به بالا (برای 0.x، همان نسخه‌ی فرعی)
"~1.2"هر 1.2.x
">=1.2"1.2.0 یا هر نسخه‌ی جدیدتر

دستور salam add github.com/owner/repo@^1.2 بازه را می‌نویسد و تازه‌ترین انتشار درون آن را قفل می‌کند. فایل قفل همیشه یک نسخه‌ی دقیق دارد، پس ساخت‌ها تا وقتی salam update نزده‌اید تکرارپذیر می‌مانند.

$ salam add github.com/demo/[email protected]
added github.com/demo/slug v1.0.0
$ salam update
updated github.com/demo/slug v1.0.0 -> v1.1.0

دستور salam update خودش هرگز به نسخه‌ی اصلی بعدی نمی‌رود. وجودش را خبر می‌دهد و تصمیم را به شما می‌سپارد:

$ salam update
  github.com/demo/slug v2.0.0 is out; it may break your code, move with: salam update github.com/demo/slug@latest
github.com/demo/slug is up to date (v1.1.0)
$ salam update github.com/demo/slug@latest
updated github.com/demo/slug v1.1.0 -> v2.0.0

مخزنی که هیچ تگی ندارد هم قابل افزودن است. سلام در این حالت کامیت دقیق را با یک نسخه‌ی مجازی مثل v0.0.0-8177964855e1 ثبت می‌کند و salam update آن را به تازه‌ترین کامیت می‌برد. وقتی بسته هنوز نوپاست این کار راحت است، و دلیل خوبی است که انتشار اول را زود تگ بزنید.

برای نویسنده، نسخه‌ی اصلی تازه یعنی نشانی تازه‌ی وارد کردن. import "github.com/owner/repo/v2" فقط تگ‌های v2 را می‌پذیرد و یک برنامه می‌تواند v1 و v2 را کنار هم داشته باشد. کد v2 می‌تواند در پوشه‌ی v2/ یا در ریشه‌ی مخزن باشد؛ برای استفاده در کنار v1 ورودی‌اش repo2.salam نام می‌گیرد و package repo2 اعلام می‌کند.

وقتی مشکلی پیش می‌آید

پیامدلیل و کار درست
package '...' has no entry file (looked for '.../argon.salam')مخزن از چیدمان بخش نوشتن یک بسته پیروی نمی‌کند: import "github.com/owner/name" فایل name.salam را در سطح بالای مخزن بار می‌کند. salam add با این حال موفق می‌شود، چون فقط دانلود می‌کند. از نویسنده بخواهید فایل ورودی را اضافه کند، یا فایل‌ها را همان‌طور که README خود بسته می‌گوید با فراخوانی به کار ببرید
package '...' is not a dependency of this project; run: salam add ...این import در salam.toml نیست. دستوری را که چاپ شده اجرا کنید، یا از پوشه‌ی خود پروژه کار کنید
'name' is not exported by package '...' (mark it 'pub')بسته آن نام را خصوصی نگه داشته است. از رابط همگانی‌اش استفاده کنید، یا از نویسنده بخواهید آن را همگانی کند
salam: cannot list the versions of https://github.com/...نشانی غلط نوشته شده، مخزن خصوصی است و اعتبارنامه ندارید، یا گیت به میزبان نمی‌رسد
salam: git could not fetch v9.0.0 from ...چنین تگی وجود ندارد. تگ‌های مخزن را ببینید، یا نسخه را ننویسید تا تازه‌ترین گرفته شود
no main.salam in '...': a project's entry file must be named main.salamsalam build یا salam run را بیرون از پروژه زده‌اید. با salam init پروژه بسازید
'...' has no main.salam, so there is no program to installsalam install برای برنامه‌هاست. کتابخانه را با salam add به کار ببرید
needs Salam X or newer; run: salam upgradeخط salam در مانیفست بسته، مترجم جدیدتری از مترجم شما می‌خواهد

چند نکته‌ی دیگر که بعضی غافلگیری‌ها را توضیح می‌دهد:

  • تگی که بعد از انتشار جابه‌جا شده باشد چک‌سام دیگری می‌دهد و دستور به‌جای ساختن کد متفاوت، متوقف می‌شود. اگر نسخه‌ی تازه درست است، خطش را از ~/.salam/pkg/sums.txt پاک کنید.
  • اگر گیت روی ماشین نباشد، بسته‌های گیت‌هاب، گیت‌لب و کدبرگ به‌صورت آرشیو دانلود می‌شوند. GITHUB_TOKEN سقف درخواست گیت‌هاب را برای فهرست تگ‌ها بالا می‌برد و SALAM_NO_GIT=1 مسیر آرشیو را اجباری می‌کند.
  • SALAM_PKG پوشه‌ی دانلودها را عوض می‌کند و salam deps clean آن را خالی می‌کند.
  • اگر به نظر می‌رسد ساخت از کد قدیمی استفاده می‌کند، پوشه‌ی .salam-build پروژه را پاک کنید و دوباره بسازید.

دنبال نمونه می‌گردید؟ بسته‌های کتابخانه‌ی استاندارد دقیقاً به همین شکل نوشته شده‌اند: یک پوشه برای هر بسته و فایل ورودی هم‌نام آن. در مرجع کتابخانه ببینیدشان.