Синхронізуйте пакети між різними машинами


57

Я використовую emacs в різних місцях, і я хочу, щоб повсюди були встановлені подібні установки та пакети. Я думаю, що я можу використовувати сховище управління версіями для файлів налаштування. Оскільки я використовую Prelude , це було б ~/.emacs.d/personal/.

Я не знаю, як з пакетами. Чи є десь файл .emacs.d/із переліком встановлених пакетів, які я можу використовувати, щоб зробити emacs в інших машинах, щоб також встановити ті, що перераховані там?


2
Для мене встановлені пакети - це лише частина мого сховища контролю версій Emacs. Я байт-компілюють пакунки з найдавнішою версією Emacs, яку я хочу / потрібно використовувати, а також розміщу файли .elc у сховищі. Це забезпечує максимальний контроль та послідовність. Компроміс - це (відносно) великий розмір сховища. Мій 6-річний сховище git має розмір 120 Мб. Хоча я, мабуть, міг би піти з 1/10 його, якщо я не включав би пакунки, ці кілька "витрачених" мегабайт насправді мене не хвилюють.
паприка

1
Варто додати, що хоча ELPA / MELPA / ... сьогодні досить популярні, все ще не всі пакети доступні через них. Отже, якщо ви використовуєте пакет, який потребує встановлення вручну, можливо, ви не захочете повторити зусилля на кожній новій машині, яку ви використовуєте (плюс для кожної оновлення пакета). Знову ж таки, просте рішення - додати пакунок до вашого сховища контролю версій Emacs.
паприка

Ви не можете просто байтувати компіляцію на сайті і піти від ігнорування .elc файлів у git?
Вамсі

@Vamsi: Я поміщаю файли, складені байтами, у сховище, тому що деякі (не-ELPA) пакети трохи складні для компіляції через їх залежність. Для цього я не люблю повторювати процес, якщо не потрібно.
паприка

@paprika Чи можете ви надати деякі деталі, як отримати налаштування? Ви додаєте все у .emacs.d плюс .emacs до сховища, і це все?
Початківцю

Відповіді:


50

Немає автоматично згенерованого файлу маніфестів, який можна синхронізувати для досягнення бажаного ефекту.

Це означає, що ви можете зробити додавання дзвінків package-installу самій конфігурації emacs.

(package-install 'auctex)

Ідея полягає в тому, що package-installвона безсильна, тому якщо пакет вже присутній, насправді нічого не відбудеться. Якщо припустити, що ви маєте такий виклик для кожного пакету, який ви використовуєте (або принаймні листя у графіку залежності), який би ефективно синхронізував ваші пакунки на всіх машинах.


Для декількох пакетів можна використовувати наступні:

(setq my-package-list '(package1 package2 packageN))
(mapc #'package-install my-package-list)

2
Я здивований, наскільки простіший такий підхід порівняно із запропонованим тут рішенням . Чи є різниця? Також використовується фрагмент package-install.
Stenskjaer

1
З того package.elчасу пов'язана відповідь змінилася . Цілком можливо, що на той час package-installвиконувались операції над існуючими пакетами, а не лише видаленими.
Джонатан Ліч-Пепін

3
Ця методика, на жаль, є проблематичною - навіть якщо сховище архівів пакетів-архівів є рівномірним між машинами та визначено в SCM. Це не гарантує, що версії пакетів однакові між машинами. Проблема полягає в тому, що версії пакету не вказані; ці окремі пакети можуть з часом розходитися і їх залежності можуть стати несумісними. Це може статися досить легко в архівах активних пакетів, таких як melpa.
ctpenrose

@ctpenrose: Чи є у вас пропозиція уникнути цієї проблеми?
студент

@student Я мінімізував проблему, використовуючи пакети, стійкі до melpa та оновлення, рідше.
ctpenrose

34

Я зберігаю свою .emacs.d каталог в контролі версій. Потім у своєму init.el та наступних файлах я використовую use-package для визначення налаштування пакету. Мало того, що use-package ліниво завантажує ваші пакунки, він завантажує їх на вимогу, якщо вони не існують з усіх репост пакетів, які ви встановили.

Наприклад, я використовую go-mode, але не на кожній машині. У своєму init.el у мене є таке:

(use-package go-mode
  :ensure t
  :config
  (progn
    (defun my-go-mode-hook ()
      (linum-mode t)
      (setq tab-width 4)
      (add-hook 'before-save-hook 'gofmt-before-save))
    (add-hook 'go-mode-hook 'my-go-mode-hook)))

Це додає гачок режиму, але що ще важливіше, вказавши, що :ensure tвін завантажить пакет на вимогу.

Щоб синхронізувати машину, ви можете просто взяти замовлення або витягнути з репо і запустити Emacs. Будь-які нові пакети будуть завантажені та встановлені.


Це рішення, яке я зараз використовую, а не Cask, насамперед тому, що (як зазначив Т. Веррон) Cask не працює (добре) на Windows, і це ще одна залежність.
Енді

1
Це я також використовую, але замість того, :ensure go-modeщоб повторювати назву пакета, ви можете просто вказати:ensure t
Pedro Luz

Влучне зауваження! Це стара відповідь. Я його оновлю.
Еларсон

Ви також можете використовувати :hookключове слово для спрощення коду.
Гільгерме Саломе

17

У Emacs-25 є змінна package-selected-packages, так що ви можете налаштувати цю змінну і використовувати, package-install-selected-packagesщоб переконатися, що вони встановлені.


Зауважте, що я бачу це, назва цієї команди трохи заплутане. Чи можу я змінити його на package-install-selected-пакети?
Малабарба

Якщо припустити, що ви маєте на увазі "зараз" замість "Примітка", так.
Стефан

9

Що ви хочете використовувати, це Cask , який дозволяє створити файл Cask із зазначенням пакетів для встановлення cask install. Він може бути використаний для управління залежностями пакета та "залежностями" вашої конфігурації Emacs з легкістю. Покладіть свій файл Cask під контроль версій та встановіть / оновіть пакети на машині.


4
Варто зазначити, що (на сьогодні) це рішення не працює для віконних машин.
Т. Веррон

1
Я більше не використовую це рішення, саме з вашої причини (і що Cask - це ще одна залежність). Чудово підходить для виготовлення пакетів; жахливо для управління конфігурацією.
Енді

6

Альтернативний підхід був би таким: так як я не тільки хочу , щоб синхронізувати свої пакети Emacs, але і інші файли (наприклад .emacs, .bashrc, а й інші каталоги) між моїм сервером і моїм ноутбуком, я почав використовувати unison, щоб синхронізувати файли і довідники. Тож, працюючи на своєму ноутбуці, я пробігаю unison laptopперед усім. Мій ~/.unison/laptop.prfфайл має такий розділ для пов'язаних файлів Emacs:

path = .emacs
path = .emacs.d
ignore = Path {.emacs.d/semanticdb}

Оскільки мої пакети Emacs (а також мої резервні копії та закладки Emacs) зберігаються в ~/.emacs.dцьому, я гарантую, що у мене є все на всіх моїх машинах.

Альтернативним підходом було б покласти .emacs.dкаталог у каталог, який синхронізується з OwnCloud, DropBox або будь-яким іншим сервісом синхронізації файлів, а потім створити посилання з ~/.emacs.dцього спільного каталогу.


5

Хоча package.elце стандартний спосіб встановлення пакетів, ви також можете спробувати, el-getщо дуже корисно для встановлення пакунків, які не є (або не можуть бути) в elpa. Ця відповідь стосується синхронізації таких пакетів.

Те, як ви гарантуєте, що дані пакети встановлені під час використання el-get, - це додати в файл init щось подібне до наступного

(el-get 'sync '(packages))

де пакети - це список пакетів, які ви хочете встановити. Ця функція подібна до того, що package-installвона встановлює пакети лише в тому випадку, якщо вони вже не встановлені, інакше вона просто ініціалізує пакети.


5

Я використовую невеликий трюк, "викрадений" у emacs-starter-kit (я думаю):

(defun maybe-install-and-require (p)
  (when (not (package-installed-p p))
   (package-install p))
  (require p))

Тому коли мені потрібен пакет, я просто використовую:

(maybe-install-and-require 'magit)

У запуску Emacs, оцінюючи мій конфігурацію, package.elпередбачено встановити magit, якщо він не встановлений.

Ви можете знайти мою конфігурацію тут:

https://github.com/mdallastella/emacs-config/


1
Дотримуючись тієї самої філософії, ви можете використовувати "парадокс-вимагати" від парадокса
csantosb

3

У мене є каталог ~ / emacs, який контролюється версіями ртутної версії і містить все, з чого складається моя програма emacs (~ / emacs / site-lisp для завантажуваних вручну бібліотек, ~ / emacs / elpa для встановлених elpa бібліотек, ~ / emacs / тощо / для розділених .emacs, ~ / emacs / dot-emacs.el, які я позначаю як ~ / .emacs). Потрібно було переробити деякі пакунки, щоб мати все важливі файли всередині цього дерева, але воно працює добре. Ті декілька бітів, які є машинними, я реалізував умовами назви системи.

Тож після того, як я інсталюю / перенастроюю / зміняю що-небудь, я просто здійснюю потягнення / натискання всіх змін між усіма машинами, якими я користуюся.

Додатковою перевагою є те, що я маю повну історію мого конфігурації і можу повернутися назад / бісект / повернутись у випадку, якщо щось піде не так.

PS mercurial здається особливо підходящим, оскільки він має природний двосторонній потяг / натиск, але подібні налаштування не повинні бути важкими для досягнення git або будь-якого іншого dvcs.


3

У мене це є setup-packages.elв моїй програмі Emacs, яка є гібридом коду з блогу Prelude та блогу Tomorokoshi про управління пакетами .

setup-packages.el робить наступне:

  • Створіть каталог для elpaпакунків, якщо такого не існує, і додайте його та його підкаталоги до load-path.
  • Оновіть package-archivesсписок за допомогою Melpa.
  • Перевірте, чи встановлені всі пакети, вказані у my-packagesсписку. Якщо пакет не встановлений, встановіть його.

Як реалізувати

  • Збережіть setup-packages.elнижче у своєму ~/.emacs.d/каталозі.
  • Встановити user-emacs-directory, setup-packages-fileі my-packagesзмінні в init.elі робити (load setup-packages-file).

Коли ви вперше запускаєте emacs на машині, на якій не встановлені ці пакунки, усі перелічені пакунки my-packagesбудуть встановлені автоматично.

setup-пакети.el

;; setup-packages.el - Package management

(require 'cl)
(require 'package)

;; Set the directory where you want to install the packages
(setq package-user-dir (concat user-emacs-directory "elpa/"))

;; Add melpa package source when using package list
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/") t)

;; Load emacs packages and activate them
;; This must come before configurations of installed packages.
;; Don't delete this line.
(package-initialize)
;; `package-initialize' call is required before any of the below
;; can happen

;; Auto install the required packages
;; Method to check if all packages are installed
(defun packages-installed-p ()
  (loop for p in my-packages
        when (not (package-installed-p p)) do (return nil)
        finally (return t)))

;; if not all packages are installed, check one by one and install the missing ones.
(unless (packages-installed-p)
  ;; check for new packages (package versions)
  (message "%s" "Emacs is now refreshing its package database...")
  (package-refresh-contents)
  (message "%s" " done.")
  ;; install the missing packages
  (dolist (p my-packages)
    (when (not (package-installed-p p))
      (package-install p))))

(provide 'setup-packages)

init.el

Вам знадобиться init.el:

(setq user-home-directory  (getenv "HOME"))
(setq user-emacs-directory (concat user-home-directory ".emacs.d/"))
(setq setup-packages-file  (expand-file-name "setup-packages.el" user-emacs-directory))

;; A list of packages to ensure are installed at launch
(setq my-packages
      '(
        ;; package1
        ;; package2
       ))

(load setup-packages-file nil :nomessage) ; Load the packages

2

Щоб віддзеркалити свою конфігурацію, я вирішив піти на інший підхід, використовуючи Syncthing ; кожна зміна будь-якого з моїх конфігураційних файлів поширюється на будь-який інший мій ПК, не піклуючись про це, тому при оновленні пакетів я повинен робити це лише в одному з ПК.


2

RSYNC : Синхронізуйте вибрані папки / файли за rsyncдопомогою домашньої мережі або через sshвіддалений сервер.

rsyncце одностороння утиліта синхронізації, яка здатна видаляти файли на цільовому рівні, тому, будь ласка, не забудьте зробити резервну копію даних як у вихідному, так і в цільовому розташуванні та ретельно протестуйте за допомогою цієї --dry-runопції, перш ніж робити справжню справу.

Щоб прочитати про те, як правильно налаштувати .authinfoфайл, див. Https://www.gnu.org/software/emacs/manual/auth.html Приклад .authinfoвмісту файлу (який може містити кілька різних записів) виглядає наступним чином:

machine mymachine login myloginname password mypassword port myport

Налаштуйте та використовуйте функцію rsync-remoteдля синхронізації sshз віддаленим сервером. Або скористайтеся функцією rsync-localдля синхронізації на тому ж комп’ютері або через надійну домашню мережу.

(require 'auth-source)

;;; EXAMPLE:
;;;   (get-auth-info "12.34.567.89" "username")
;;;   (get-auth-info "localhost" "root")
(defun get-auth-info (host user &optional port)
  (let ((info (nth 0 (auth-source-search
                      :host host
                      :user user
                      :port port
                      :require '(:user :secret)
                      :create t))))
    (if info
      (let* ((port (plist-get info :port))
             (secret-maybe (plist-get info :secret))
             (secret
               (if (functionp secret-maybe)
                 (funcall secret-maybe)
                 secret-maybe)))
          (list port secret))
    nil)))

(defun rsync-filter (proc string)
  (cond
    ((string-match
       "^\\([a-zA-Z0-9_\\-\\.]+\\)@\\([a-zA-Z0-9_\\-\\.]+\\)'s password: "
       string)
      (let* ((user (substring string (match-beginning 1) (match-end 1)))
             (host (substring string (match-beginning 2) (match-end 2)))
             (password (car (cdr (get-auth-info host user)))))
        (process-send-string proc (concat password "\n"))))
    ((not (or (string-match "files\\.\\.\\.\r" string)
              (string-match "files to consider\n" string)))
      (with-current-buffer (messages-buffer)
        (let ((inhibit-read-only t))
          (goto-char (point-max))
          (when (not (bolp))
            (insert "\n"))
          (insert string)
          (when (not (bolp))
            (insert "\n")))))))

(defun rsync-remote ()
"Use rsync to a remote server via ssh.  Back-up your data first!!!"
(interactive)
  (let* (
      (host "localhost")
      (username "root")
      (port (or (car (get-auth-info host username))
                (number-to-string (read-number "Port:  "))))
      (source
        (let ((dir (expand-file-name (locate-user-emacs-file "elpa/"))))
          (if (file-directory-p dir)
            dir
            (let ((debug-on-quit nil)
                  (msg (format "`%s` is not a valid directory." dir)))
              (signal 'quit `(,msg))))))
      (target "/private/var/mobile/elpa/")
      (ssh "/usr/bin/ssh")
      (rsync "/usr/bin/rsync")
      (rsync-include-file "/path/to/include-file.txt")
      (rsync-exclude-file "/path/to/exclude-file.txt")
      (rsh (concat "--rsh=ssh -p " port " -l " username))
      (host+target (concat host ":" target)))
    (start-process
        "rsync-process"
        nil
        rsync
        "-avr" ;; must specify the `-r` argument when using `--files-from`
        "--delete"
        ;; The paths inside the exclusion file must be relative, NOT absolute.
        ;;; (concat "--files-from=" rsync-include-file)
        ;;; (concat "--exclude-from=" rsync-exclude-file)
        rsh
        source
        host+target)
    (set-process-filter (get-process "rsync-process") 'rsync-filter)
    (set-process-sentinel
      (get-process "rsync-process")
      (lambda (p e) (when (= 0 (process-exit-status p))
        (message "rsync-remote:  synchronizing ... done."))))))

(defun rsync-local ()
"Use rsync locally -- e.g., over a trusted home network.
 Back-up your data first!!!"
  (interactive)
  (let (
      (rsync-program "/usr/bin/rsync")
      (source
        (let ((dir (expand-file-name
                     (file-name-as-directory
                       (read-directory-name "Source Directory: " nil nil nil nil)))))
          (if (file-directory-p dir)
            dir
            (let ((debug-on-quit nil)
                  (msg (format "`%s` is not a valid directory." dir)))
              (signal 'quit `(,msg))))))
      (target (expand-file-name
                (file-name-as-directory
                  (read-directory-name "Target Directory: " nil nil nil nil)))))
    (unless (y-or-n-p (format "SOURCE:  %s | TARGET:  %s" source target))
      (let ((debug-on-quit nil))
        (signal 'quit `("You have exited the function."))))
    (start-process "rsync-process"
      nil
      rsync-program
      "--delete"
      "-arzhv"
      source
      target)
    (set-process-filter (get-process "rsync-process") #'rsync-process-filter)
    (set-process-sentinel
      (get-process "rsync-process")
      (lambda (p e)
        (when (= 0 (process-exit-status p))
        (message "Done!"))))))

0

https://github.com/redguardtoo/elpa-mirror створює локальне сховище всіх встановлених пакетів.

Використання просте, просто запустіть M-x elpamr-create-mirror-for-installed.

На інших машинах вставте (setq package-archives '(("myelpa" . "~/myelpa/")))у свій .emacsі перезапустіть Emacs.

Тепер на всіх машинах ви отримуєте точно таку ж версію пакетів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.