Sincronice paquetes entre diferentes máquinas

57

Uso emacs en diferentes lugares y quiero tener una configuración y paquetes similares instalados en todas partes. Supongo que puedo usar un repositorio de control de versiones para los archivos de configuración. Desde que uso Prelude , eso sería ~/.emacs.d/personal/.

No sé cómo hacer con los paquetes. ¿Hay algún archivo en algún lugar .emacs.d/con la lista de paquetes instalados que pueda usar para hacer emacs en otras máquinas para instalar también los que figuran allí?

El Diego Efe
fuente
2
Para mí, los paquetes instalados son solo parte de mi repositorio de control de versiones de Emacs. Compilo byte los paquetes con la versión más antigua de Emacs que quiero / necesito usar y también pongo los archivos .elc en el repositorio. Esto ofrece el máximo control y consistencia. La compensación es el tamaño (relativamente) grande del repositorio. Mi repositorio git de 6 años tiene un tamaño de 120 MB. Si bien probablemente podría salir con 1/10 si no incluyera los paquetes, esos pocos megabytes "desperdiciados" realmente no me preocupan.
pimentón
1
Debo agregar que si bien ELPA / MELPA / ... son bastante populares hoy en día, todavía no todos los paquetes están disponibles a través de ellos. Entonces, si usa un paquete que necesita instalación manual, es posible que no desee replicar el esfuerzo en cada nueva máquina que use (más para cada actualización de paquete). Una vez más, una solución fácil es agregar el paquete a su repositorio de control de versiones de Emacs.
pimentón
¿No puede simplemente compilar byte en el sitio e ignorar los archivos .elc en git?
Vamsi
@Vamsi: puse los archivos compilados en bytes en el repositorio porque algunos paquetes (no ELPA) son un poco complicados de compilar debido a sus dependencias. Para estos no me gusta repetir el proceso si no es necesario.
pimentón
@paprika ¿Podría proporcionar algunos detalles sobre cómo obtener su configuración? ¿Agrega todo en .emacs.d más .emacs al repositorio y listo?
Principiante

Respuestas:

50

No hay un archivo de manifiesto generado automáticamente que pueda sincronizar para lograr el efecto deseado.

Dicho esto, algo que puede hacer es agregar llamadas a package-installsu propia configuración de emacs.

(package-install 'auctex)

La idea es que package-installes idempotente, por lo que si el paquete ya está presente, en realidad no pasará nada. Suponiendo que tenga esa llamada para cada paquete que use (o al menos las hojas en el gráfico de dependencia), eso sincronizaría efectivamente sus paquetes entre las máquinas.


Para múltiples paquetes puede usar lo siguiente:

(setq my-package-list '(package1 package2 packageN))
(mapc #'package-install my-package-list)
Sigma
fuente
2
Me sorprende cuánto más simple se compara este enfoque con la solución sugerida aquí . ¿Hay alguna diferencia? El fragmento también usa package-install.
Stenskjaer
1
Ha habido cambios package.eldesde esa respuesta vinculada. Es posible que en ese momento package-installrealizara operaciones en paquetes existentes, no solo desinstalados.
Jonathan Leech-Pepin
3
Desafortunadamente, esta técnica es problemática, incluso si el repositorio de archivos de paquetes es uniforme entre las máquinas y se especifica en SCM. No asegura que las versiones del paquete sean idénticas entre las máquinas. El problema es que las versiones del paquete no están especificadas; Estos paquetes individuales pueden divergir con el tiempo y sus dependencias pueden volverse incompatibles. Esto puede suceder con bastante facilidad en archivos de paquetes activos como melpa.
ctpenrose
@ctpenrose: ¿Tiene alguna sugerencia para evitar este problema?
estudiante
@student He minimizado el problema usando melpa-stable y actualizando los paquetes con menos frecuencia.
ctpenrose
34

Mantengo mi directorio .emacs.d en control de versiones. Luego, en mi init.el y archivos posteriores, uso use-package para definir la configuración del paquete. Use-package no carga de forma perezosa sus paquetes, los descargará a pedido si no existen desde cualquier repositorio de paquetes que haya configurado.

Por ejemplo, uso el modo go, pero no en todas las máquinas. En mi init.el tengo lo siguiente:

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

Esto agrega un enlace de modo, pero más importante, al especificar :ensure tque descargará el paquete a pedido.

Para mantener una máquina sincronizada, solo puede realizar el pago o extraer del repositorio e iniciar Emacs. Cualquier paquete nuevo se descargará e instalará.

Elarson
fuente
Esta es la solución que ahora uso también, en lugar de Cask, principalmente porque (como señaló T. Verron) Cask no funciona (bien) en Windows, y es otra dependencia más.
Andy
1
Esta es la forma en que también lo uso, pero en lugar de :ensure go-moderepetir el nombre del paquete, puede especificar:ensure t
Pedro Luz,
¡Buen punto! Esta es una vieja respuesta. Lo actualizaré.
Elarson el
También puede usar la :hookpalabra clave para simplificar su código.
Guilherme Salomé el
17

En Emacs-25, existe la variable package-selected-packages, por lo que puede personalizarla y usarla package-install-selected-packagespara asegurarse de que esté instalada.

Stefan
fuente
Tenga en cuenta que lo veo, el nombre de ese comando es un poco confuso. ¿Puedo cambiarlo a package-install-selected-packages?
Malabarba
Suponiendo que quiere decir "Ahora" en lugar de "Nota", sí.
Stefan
9

Lo que desea usar es Cask , que le permite crear un archivo Cask que especifique en qué paquetes instalar cask install. Se puede usar para administrar las dependencias de un paquete y las "dependencias" de su configuración de Emacs con facilidad. Ponga su archivo Cask bajo control de versiones e instale / actualice paquetes por máquina.

Andy
fuente
44
Vale la pena señalar que (a partir de hoy) esta solución no funciona para máquinas con Windows.
T. Verron
1
Ya no uso esta solución, por su propia razón (y que Cask es otra dependencia más). Genial para hacer paquetes; horrible para la gestión de la configuración.
Andy
6

Un enfoque alternativo sería la siguiente: puesto que no sólo quiero sincronizar los paquetes de Emacs, pero también otros archivos (por ejemplo .emacs, .bashrcsino también otros directorios) entre mi servidor y mi portátil, empecé a utilizar unison, para sincronizar los archivos y directorios. Entonces, cuando trabajo en mi computadora portátil, simplemente corro unison laptopantes que nada. Mi ~/.unison/laptop.prfarchivo tiene la siguiente sección para archivos relacionados con Emacs:

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

Dado que mis paquetes de Emacs (y también mis copias de seguridad y marcadores de Emacs) están almacenados en ~/.emacs.desto, me asegura que tengo todo en todas mis máquinas.

Un enfoque alternativo sería colocar el .emacs.ddirectorio en un directorio que esté sincronizado con OwnCloud, DropBox o cualquier otro servicio de sincronización de archivos y luego crear enlaces simbólicos desde ~/.emacs.dese directorio compartido.

ph0t0nix
fuente
5

Si bien package.eles la forma estándar de instalar paquetes, es posible que también desee probar el-getcuál es muy útil para instalar paquetes que no están (o no pueden estar) en elpa. Esta respuesta trata sobre la sincronización de dichos paquetes.

La forma en que se asegura de que se instalen determinados paquetes al usar el-get es agregar algo como lo siguiente a su archivo init

(el-get 'sync '(packages))

donde paquetes son la lista de paquetes que desea instalar. Esta función es similar a la package-installque instala los paquetes solo si aún no están instalados, de lo contrario, simplemente inicializa los paquetes.

Iqbal Ansari
fuente
5

Uso un pequeño truco "robado" de emacs-starter-kit (creo):

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

Entonces, cuando necesito un paquete, simplemente uso:

(maybe-install-and-require 'magit)

En los inicios de emacs, evaluar mi configuración package.elproporcionará instalar magit si no está instalado.

Puedes encontrar mi configuración aquí:

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

mdallastella
fuente
1
Siguiendo la misma filosofía, podría usar 'paradox-require' de paradox
csantosb
3

Tengo el directorio ~ / emacs que está controlado por la versión mercurial y contiene todo lo que mi configuración de emacs consiste en (~ / emacs / site-lisp para bibliotecas descargadas manualmente, ~ / emacs / elpa para bibliotecas instaladas por elpa, ~ / emacs / etc / para .emacs divididos, ~ / emacs / dot-emacs.el que enlace simbólicamente como ~ / .emacs). Se requirió algunos ajustes de algunos paquetes para tener todos los archivos importantes dentro de este árbol, pero funciona bien. Esos pocos bits que son específicos de la máquina los he implementado por condicionales en el nombre del sistema.

Entonces, después de instalar / reconfigurar / cambiar cualquier cosa, simplemente confirmo pull / push todos los cambios entre todas las máquinas que uso.

Un beneficio adicional es que tengo un historial completo de mi configuración y puedo retroceder / bisecar / revertir en caso de que algo salga mal.

PS mercurial parece particularmente adecuado, ya que tiene un tirón / empuje natural de dos lados, pero una configuración similar no debería ser difícil de lograr con git o cualquier otro dvcs.

Mekk
fuente
3

Tengo esto setup-packages.elen mi configuración de emacs, que es un híbrido de código del blog de Prelude y Tomorokoshi sobre Package Management .

setup-packages.el hace lo siguiente:

  • Cree un directorio para elpapaquetes si no existe y agréguelo junto con sus subdirectorios al load-path.
  • Actualizar package-archiveslista con Melpa.
  • Compruebe si tiene my-packagesinstalados todos los paquetes enumerados en la lista. Si un paquete no está instalado, instálelo.

Cómo implementar

  • Guarde lo setup-packages.elsiguiente en su ~/.emacs.d/directorio.
  • Conjunto user-emacs-directory, setup-packages-filey my-packagesvariables en su init.ely hacer (load setup-packages-file).

Cuando inicia emacs por primera vez en una máquina que no tiene estos paquetes instalados, todos los paquetes enumerados my-packagesse instalarán automáticamente.

setup-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

Necesitaría lo siguiente en su 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
Kaushal Modi
fuente
2

Para reflejar mi configuración, decidí adoptar un enfoque diferente, usando Syncthing ; cada cambio en cualquiera de mis archivos de configuración se propaga a cualquier otra de mis PC sin tener que preocuparme por eso, así que cuando actualizo paquetes solo tengo que hacerlo en una de las PC.

csantosb
fuente
2

RSYNC : Sincronice carpetas / archivos seleccionados utilizando rsync, ya sea a través de una red doméstica o mediante sshun servidor remoto.

rsynces una utilidad de sincronización unidireccional que puede eliminar archivos en el destino, así que asegúrese de hacer una copia de seguridad de sus datos en las ubicaciones de origen y destino, y pruebe a fondo utilizando la --dry-runopción, antes de hacer lo real.

Para leer sobre cómo configurar correctamente el .authinfoarchivo, consulte https://www.gnu.org/software/emacs/manual/auth.html. El .authinfocontenido de un archivo de ejemplo (que puede contener varias entradas diferentes) es el siguiente:

machine mymachine login myloginname password mypassword port myport

Configure y use la función rsync-remotepara sincronizar con sshun servidor remoto. O utilice la función rsync-localpara sincronizar en la misma computadora o en una red doméstica confiable.

(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!"))))))
lista de leyes
fuente
0

https://github.com/redguardtoo/elpa-mirror crea un repositorio local de todos los paquetes instalados.

El uso es simple, solo corre M-x elpamr-create-mirror-for-installed.

En otras máquinas, inserte (setq package-archives '(("myelpa" . "~/myelpa/")))en su .emacsy reinicie Emacs.

Ahora en todas las máquinas, obtienes exactamente la misma versión de paquetes.

Chen Bin
fuente