Синхронизировать пакеты между разными машинами

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.
Стенскьяер
1
Со времени package.elэтого связанного ответа произошли изменения . Возможно, что в данный момент package-installвыполняются операции с существующими пакетами, а не только с неустановленными.
Джонатан Лич-Пепин
3
Эта методика, к сожалению, проблематична - даже если репозиторий архивов пакетов одинаков между компьютерами и указан в SCM. Это не гарантирует, что версии пакетов идентичны между машинами. Проблема в том, что версии пакетов не указаны; эти отдельные пакеты могут со временем расходиться, и их зависимости могут стать несовместимыми. Это может произойти довольно легко в архивах активных пакетов, таких как melpa.
ctpenrose
@ctpenrose: У вас есть предложение, чтобы избежать этой проблемы?
ученик
@student Я минимизировал проблему, используя melpa-stable и обновляя пакеты реже.
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. Любые новые пакеты будут загружены и установлены.

elarson
источник
Это решение, которое я сейчас использую, а не 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-packages?
Малабарба
Предполагая, что вы имеете в виду «сейчас» вместо «Примечание», да.
Стефан,
9

То, что вы хотите использовать, - это Cask , который позволяет вам создать файл Cask, указывающий, на какие пакеты устанавливать cask install. С его помощью можно легко управлять зависимостями пакета и «зависимостями» вашей конфигурации Emacs. Поместите свой файл Cask в систему контроля версий и устанавливайте / обновляйте пакеты для каждого компьютера отдельно.

Энди
источник
4
Стоит отметить, что (на сегодняшний день) это решение не работает для Windows-машин.
Т. Веррон
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на этот общий каталог.

ph0t0nix
источник
5

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

Вы гарантируете, что указанные пакеты установлены при использовании el-get, добавив в файл инициализации что-то вроде следующего

(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/

mdallastella
источник
1
Следуя той же философии, вы можете использовать 'paradox-require' из paradox
csantosb
3

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

Поэтому после того, как я что-то установил / переконфигурировал / изменил, я просто фиксирую изменения / подталкивание всех изменений между всеми машинами, которые я использую.

Дополнительным преимуществом является то, что у меня есть полная история моей конфигурации и я могу вернуться / bisect / revert, если что-то пойдет не так.

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будут автоматически установлены.

Настройка-packages.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 ; каждое изменение в любом из моих конфигурационных файлов распространяется на любые другие мои ПК, не заботясь об этом, поэтому, когда я обновляю пакеты, мне нужно сделать это только в одном из ПК.

csantosb
источник
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!"))))))
lawlist
источник
0

https://github.com/redguardtoo/elpa-mirror создает локальный репозиторий всех установленных пакетов.

Использование простое, просто запустить M-x elpamr-create-mirror-for-installed.

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

Теперь на всех машинах вы получаете абсолютно одинаковую версию пакетов.

чен бен
источник