Sincronizar pacotes entre máquinas diferentes


57

Eu uso o emacs em lugares diferentes e quero ter uma instalação e pacotes semelhantes instalados em todos os lugares. Eu acho que posso usar um repositório de controle de versão para os arquivos de instalação. Desde que eu uso Prelude , isso seria ~/.emacs.d/personal/.

Eu não sei como fazer com pacotes. Existe um arquivo em algum lugar .emacs.d/com a lista de pacotes instalados que eu posso usar para criar o emacs em outras máquinas e também instalar os listados lá?


2
Para mim, os pacotes instalados são apenas parte do meu repositório de controle de versão do Emacs. Byte-compile os pacotes com a versão mais antiga do Emacs que eu quero / preciso usar e também coloquei os arquivos .elc no repositório. Isso oferece controle e consistência máximos. O trade-off é o tamanho (relativamente) grande do repositório. Meu repositório git de 6 anos tem um tamanho de 120 MB. Embora eu provavelmente consiga usar 1/10, se não incluir pacotes, esses poucos megabytes "desperdiçados" realmente não me preocupam.
paprika

1
Devo acrescentar que, embora ELPA / MELPA / ... sejam bastante populares hoje em dia, nem todos os pacotes estão disponíveis através deles. Portanto, se você usar um pacote que precise de instalação manual, poderá não querer replicar o esforço em cada nova máquina usada (mais para cada atualização do pacote). Novamente, uma solução fácil é adicionar o pacote ao seu repositório de controle de versão do Emacs.
paprika

Você não pode simplesmente compilar o byte no site e ignorar arquivos .elc no git?
Vamsi 28/09

@Vamsi: Coloquei os arquivos compilados em bytes no repositório porque alguns pacotes (não ELPA) são um pouco complicados de serem compilados devido a suas dependências. Para estes, não gosto de repetir o processo, se não for necessário.
paprika

@paprika Você poderia fornecer alguns detalhes sobre como obter sua configuração? Você adiciona tudo em .emacs.d mais .emacs ao repositório e é isso?
Iniciante

Respostas:


50

Não há arquivo de manifesto gerado automaticamente que você possa sincronizar para obter o efeito desejado.

Dito isto, algo que você pode fazer é adicionar chamadas package-installna própria configuração do emacs.

(package-install 'auctex)

A idéia é que package-installseja idempotente; portanto, se o pacote já estiver presente, nada acontecerá. Supondo que você tenha uma chamada para cada pacote que você usa (ou pelo menos as folhas no gráfico de dependência), isso sincronizaria efetivamente seus pacotes entre máquinas.


Para vários pacotes, você pode usar o seguinte:

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

2
Estou surpreso com o quão mais simples essa abordagem é comparada à solução sugerida aqui . Existe alguma diferença? O trecho também usa package-install.
Stenskjaer 30/09/14

1
Houve alterações package.eldesde essa resposta vinculada. É possível que, no momento, package-installexecutássemos operações em pacotes existentes, não apenas nos desinstalados.
Jonathan Leech-Pepin

3
Infelizmente, esta técnica é problemática - mesmo que o repositório de pacotes-archive seja uniforme entre as máquinas e especificado no SCM. Não garante que as versões dos pacotes sejam idênticas entre as máquinas. O problema é que as versões do pacote não são especificadas; esses pacotes individuais podem divergir com o tempo e suas dependências podem se tornar incompatíveis. Isso pode acontecer facilmente nos arquivos de pacotes ativos, como o melpa.
Ctpenrose

@ctpenrose: Você tem alguma sugestão para evitar esse problema?
estudante

@student Minimizei o problema usando pacotes melpa-stable e atualizando com menos frequência.
Ctpenrose

34

Eu mantenho meu diretório .emacs.d no controle de versão. Então, nos meus arquivos init.el e subsequentes, uso use-package para definir a configuração do pacote. O pacote de uso não apenas carrega preguiçosamente seus pacotes, como também os baixa por demanda se eles não existirem nos repositórios de pacotes que você configurou.

Por exemplo, eu uso o modo go, mas não em todas as máquinas. No meu init.el eu tenho o seguinte:

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

Isso adiciona um gancho de modo, mas mais importante, ao especificar que :ensure tele fará o download do pacote sob demanda.

Para manter uma máquina sincronizada, basta fazer checkout ou retirar do repositório e iniciar o Emacs. Quaisquer novos pacotes serão baixados e instalados.


Essa é a solução que agora uso também, e não o Cask, principalmente porque (como T. Verron notou) o Cask não funciona (bem) no Windows e é mais uma dependência.
217 Andy

1
Esta é a maneira que eu uso também, mas em vez de fazer :ensure go-modeque está repetindo o nome do pacote, você pode apenas especificar:ensure t
Pedro Luz

Bom ponto! Esta é uma resposta antiga. Vou atualizá-lo.
Elarson 11/11

Você também pode usar a :hookpalavra-chave para simplificar seu código.
Guilherme Salomé

17

No Emacs-25, existe a variável package-selected-packages, então você pode personalizá-la e usá-la package-install-selected-packagespara garantir que estejam instaladas.


Note que eu vejo, o nome desse comando é um pouco confuso. Posso alterá-lo para package-install-selected-packages?
Malabarba

Supondo que você queira dizer "Agora" em vez de "Nota", sim.
Stefan

9

O que você deseja usar é Cask , que permite criar um arquivo Cask especificando em quais pacotes instalar cask install. Ele pode ser usado para gerenciar dependências de um pacote e "dependências" da configuração do seu Emacs com facilidade. Coloque seu arquivo Cask sob controle de versão e instale / atualize os pacotes por máquina.


4
Vale ressaltar que (a partir de hoje) esta solução não funciona para máquinas Windows.
T. Verron

1
Eu não uso mais esta solução, por sua própria razão (e que Cask é mais uma dependência). Ótimo para fazer pacotes; horrível para gerenciamento de configuração.
217 Andy

6

Uma abordagem alternativa seria a seguinte: uma vez que eu não só deseja sincronizar meus pacotes do Emacs, mas também outros arquivos (por exemplo .emacs, .bashrcmas também outros diretórios) entre o meu servidor e meu laptop, eu comecei a usar unison, para sincronizar os arquivos e diretórios. Então, ao trabalhar no meu laptop, eu corro unison laptopantes de qualquer outra coisa. Meu ~/.unison/laptop.prfarquivo possui a seguinte seção para arquivos relacionados ao Emacs:

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

Como meus pacotes Emacs (e também meus backups e favoritos do Emacs) são armazenados, ~/.emacs.disso garante que eu tenha tudo em todas as minhas máquinas.

Uma abordagem alternativa seria colocar o .emacs.ddiretório em um diretório sincronizado com o OwnCloud, DropBox ou qualquer outro serviço de sincronização de arquivos e criar links simbólicos ~/.emacs.dpara esse diretório compartilhado.


5

Embora package.elseja a maneira padrão de instalar pacotes, você também pode tentar o el-getque é muito útil para instalar pacotes que não estão (ou não podem estar) no elpa. Esta resposta lida com a sincronização desses pacotes.

A maneira de garantir que os pacotes fornecidos sejam instalados ao usar o el-get é adicionar algo como o seguinte ao arquivo init

(el-get 'sync '(packages))

Onde pacotes são a lista de pacotes que você deseja instalar. Essa função é semelhante a package-installinstalar os pacotes apenas se eles ainda não estiverem instalados, caso contrário, simplesmente inicializa os pacotes.


5

Eu uso um pequeno truque "roubado" do emacs-starter-kit (eu acho):

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

Então, quando eu preciso de um pacote, eu simplesmente uso:

(maybe-install-and-require 'magit)

Nas startups do emacs, avaliando minha configuração, package.elserá possível instalar o magit, se não estiver instalado.

Você pode encontrar minha configuração aqui:

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


1
Após mesma filosofia, você poderia usar 'paradoxo-require' de paradoxo
csantosb

3

Eu tenho o diretório ~ / emacs que é controlado pela versão mercurial e contém tudo o que minha configuração do emacs consiste (~ / emacs / site-lisp para bibliotecas baixadas manualmente, ~ / emacs / elpa para bibliotecas instaladas elpa, ~ / emacs / etc / para .emacs divididos, ~ / emacs / dot-emacs.el, que eu simbolizo como ~ / .emacs). Requeria alguns ajustes em alguns pacotes para ter todos os arquivos importantes dentro dessa árvore, mas funciona bem. Os poucos bits que são específicos da máquina foram implementados por condicionais no nome do sistema.

Então, depois de instalar / reconfigurar / alterar qualquer coisa, simplesmente confirmo puxar / enviar todas as alterações entre todas as máquinas que uso.

O benefício extra é que eu tenho um histórico completo da minha configuração e posso voltar / dividir / reverter caso algo dê errado.

O PS mercurial parece particularmente adequado, pois possui puxar / empurrar nos dois lados, mas uma configuração semelhante não deve ser difícil de alcançar com o git ou qualquer outro dvcs.


3

Eu tenho isso setup-packages.elna minha configuração do emacs, que é um híbrido de código do blog de Prelude e Tomorokoshi sobre Gerenciamento de Pacotes .

setup-packages.el faz o seguinte:

  • Crie um diretório para elpapacotes, se não existir, e adicione-o e seus subdiretórios ao arquivo load-path.
  • Atualize a package-archiveslista com Melpa.
  • Verifique se você tem todos os pacotes listados na my-packageslista instalados. Se um pacote não estiver instalado, instale-o.

Como implementar

  • Salve o setup-packages.elabaixo no seu ~/.emacs.d/diretório.
  • Definir user-emacs-directory, setup-packages-filee my-packagesvariáveis em seu init.ele fazer (load setup-packages-file).

Quando você inicia o emacs pela primeira vez em uma máquina que não possui esses pacotes instalados, todos os pacakges listados em my-packagesserão instalados automaticamente.

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

Você precisaria do seguinte em seu 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

Para espelhar minha configuração, decidi adotar uma abordagem diferente, usando o Syncthing ; toda mudança em qualquer um dos meus arquivos de configuração se propaga para qualquer outro dos meus computadores sem precisar se preocupar com isso; portanto, quando eu atualizo os pacotes, só preciso fazer isso em um dos computadores.


2

RSYNC : sincronize pastas / arquivos selecionados usando rsyncuma rede doméstica ou via sshservidor remoto.

rsyncé um utilitário de sincronização unidirecional capaz de excluir arquivos no destino. Portanto, faça backup de seus dados nos locais de origem e de destino e faça um teste completo usando a --dry-runopção antes de fazer a coisa real.

Para ler sobre como configurar corretamente o .authinfoarquivo, consulte https://www.gnu.org/software/emacs/manual/auth.html Um exemplo de .authinfoconteúdo de arquivo (que pode conter várias entradas diferentes) é o seguinte:

machine mymachine login myloginname password mypassword port myport

Configure e use a função rsync-remotepara sincronizar sshcom um servidor remoto. Ou use a função rsync-localpara sincronizar no mesmo computador ou em uma rede doméstica confiável.

(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 cria um repositório local de todos os pacotes instalados.

O uso é simples, basta executar M-x elpamr-create-mirror-for-installed.

Em outras máquinas, insira (setq package-archives '(("myelpa" . "~/myelpa/")))no seu .emacse reinicie o Emacs.

Agora, em todas as máquinas, você obtém exatamente a mesma versão dos pacotes.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.