Sincronizza i pacchetti tra macchine diverse


57

Uso emacs in luoghi diversi e voglio avere una configurazione simile e pacchetti installati ovunque. Immagino di poter usare un repository di controllo versione per i file di installazione. Da quando uso Prelude , sarebbe ~/.emacs.d/personal/.

Non so come fare con i pacchetti. C'è un file da qualche parte .emacs.d/con l'elenco dei pacchetti installati che posso usare per creare emacs su altre macchine per installare anche quelli elencati lì?


2
Per me i pacchetti installati sono solo una parte del mio repository di controllo versione di Emacs. Compilare in byte i pacchetti con la versione più vecchia di Emacs che voglio / ho bisogno di usare e metto anche i file .elc nel repository. Ciò offre il massimo controllo e coerenza. Il compromesso è la dimensione (relativamente) grande del repository. Il mio repository git di 6 anni ha una dimensione di 120 MB. Anche se probabilmente potrei cavarmela con 1/10 se non includessi i pacchetti, quei pochi megabyte "sprecati" non mi preoccupano davvero.
paprika,

1
Vorrei aggiungere che mentre ELPA / MELPA / ... sono piuttosto popolari al giorno d'oggi, non tutti i pacchetti sono disponibili attraverso di loro. Pertanto, se si utilizza un pacchetto che richiede l'installazione manuale, è possibile che non si desideri replicare lo sforzo su ogni nuova macchina utilizzata (più per ogni aggiornamento del pacchetto). Ancora una volta, una soluzione semplice è quella di aggiungere il pacchetto al repository di controllo della versione di Emacs.
paprika,

Non puoi semplicemente compilare i byte sul sito e cavartela ignorando i file .elc in git?
Vamsi,

@Vamsi: ho inserito i file compilati in byte nel repository perché alcuni pacchetti (non ELPA) sono un po 'difficili da compilare a causa delle loro dipendenze. Per questi non mi piace ripetere il processo se non necessario.
paprika,

@paprika Potresti fornire alcuni dettagli su come ottenere la tua configurazione? Aggiungete tutto in .emacs.d plus .emacs al repository e il gioco è fatto?
Principiante

Risposte:


50

Non esiste un file manifest generato automaticamente che è possibile sincronizzare per ottenere l'effetto desiderato.

Detto questo, qualcosa che puoi fare è aggiungere chiamate package-installnella stessa configurazione di emacs.

(package-install 'auctex)

L'idea è che package-installè idempotente, quindi se il pacchetto è già presente, in realtà non accadrà nulla. Supponendo che tu abbia una tale chiamata per ogni pacchetto che usi (o almeno i fogli nel grafico delle dipendenze), che sincronizzerebbe efficacemente i tuoi pacchetti tra le macchine.


Per più pacchetti è possibile utilizzare quanto segue:

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

2
Sono sorpreso di quanto sia più semplice questo approccio rispetto alla soluzione suggerita qui . C'è qualche differenza? Lo snippet usa anche package-install.
Stenskjaer,

1
Ci sono state modifiche a package.elda quella risposta collegata. È possibile che al momento siano state package-installeseguite operazioni su pacchetti esistenti, non solo su quelli disinstallati.
Jonathan Leech-Pepin,

3
Questa tecnica è purtroppo problematica, anche se il repository degli archivi dei pacchetti è uniforme tra le macchine e specificato in SCM. Non garantisce che le versioni del pacchetto siano identiche tra le macchine. Il problema è che le versioni del pacchetto non sono specificate; questi singoli pacchetti possono divergere nel tempo e le loro dipendenze possono diventare incompatibili. Questo può accadere abbastanza facilmente su archivi di pacchetti attivi come melpa.
ctpenrose,

@ctpenrose: hai un suggerimento per evitare questo problema?
studente

@student Ho minimizzato il problema usando melpa-stable e aggiornando i pacchetti meno spesso.
ctpenrose,

34

Tengo la mia directory .emacs.d nel controllo della versione. Quindi, nel mio init.el e nei file successivi uso use-package per definire la configurazione del pacchetto. Use-package non solo carica pigramente i pacchetti, ma li scaricherà su richiesta se non esistono da qualsiasi repository di pacchetti impostato.

Ad esempio, utilizzo la modalità Go, ma non su tutte le macchine. Nel mio init.el ho il seguente:

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

Questo aggiunge un hook di modalità, ma soprattutto, specificando :ensure tche scaricherà il pacchetto su richiesta.

Per mantenere sincronizzata una macchina, puoi semplicemente fare il checkout o estrarre dal repository e avviare Emacs. Eventuali nuovi pacchetti verranno scaricati e installati.


Questa è la soluzione che ora uso anche io, piuttosto che Cask, principalmente perché (come notato da T. Verron) Cask non funziona (bene) su Windows, ed è ancora un'altra dipendenza.
Andy,

1
Questo è il modo in cui uso anche io, ma invece di :ensure go-moderipetere il nome del pacchetto, puoi semplicemente specificare:ensure t
Pedro Luz,

Buon punto! Questa è una vecchia risposta. Lo aggiornerò.
elarson,

Puoi anche usare la :hookparola chiave per semplificare il tuo codice.
Guilherme Salomé,

17

In Emacs-25, c'è la variabile package-selected-packages, quindi puoi personalizzare questa variabile e usarla package-install-selected-packagesper assicurarti che siano installati.


Nota che lo vedo, il nome di quel comando è un po 'confuso. Posso cambiarlo in package-install-selected-pacchetti?
Malabarba,

Supponendo che intendi "Ora" invece di "Nota", sì.
Stefan,

9

Quello che vuoi usare è Cask , che ti consente di creare un file Cask specificando su quali pacchetti installare cask install. Può essere usato per gestire facilmente le dipendenze di un pacchetto e le "dipendenze" della configurazione di Emacs. Metti il ​​tuo file Cask sotto il controllo della versione e installa / aggiorna i pacchetti in base alla macchina.


4
Vale la pena notare che (ad oggi) questa soluzione non funziona per i computer Windows.
T. Verron,

1
Non uso più questa soluzione, per la tua stessa ragione (e che Cask è ancora un'altra dipendenza). Ottimo per fare pacchetti; orribile per la gestione della configurazione.
Andy,

6

Un approccio alternativo potrebbe essere il seguente: dal momento che io non voglio solo sincronizzare i pacchetti di Emacs, ma anche altri file (ad esempio .emacs, .bashrcma anche altre directory) tra il mio server e il mio portatile, ho iniziato ad utilizzare unison, per sincronizzare i file e le directory. Quindi quando lavoro sul mio laptop corro semplicemente unison laptopprima di ogni altra cosa. Il mio ~/.unison/laptop.prffile ha la seguente sezione per i file correlati a Emacs:

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

Poiché i miei pacchetti Emacs (e anche i miei backup e segnalibri Emacs) sono archiviati in ~/.emacs.dquesto, si assicura che io abbia tutto su tutti i miei computer .

Un approccio alternativo sarebbe quello di mettere la .emacs.ddirectory in una directory che è sincronizzata con OwnCloud, DropBox o qualsiasi altro servizio di sincronizzazione dei file e quindi creare collegamenti simbolici ~/.emacs.da quella directory condivisa.


5

Mentre package.elè il modo standard per installare i pacchetti, potresti anche voler provare el-getquale è molto utile per l'installazione di pacchetti che non sono (o non possono essere) su elpa. Questa risposta riguarda la sincronizzazione di tali pacchetti.

Il modo in cui ti assicuri che determinati pacchetti siano installati quando usi el-get è quello di aggiungere qualcosa come il seguente al tuo file init

(el-get 'sync '(packages))

dove i pacchetti sono l'elenco dei pacchetti che si desidera installare. Questa funzione è simile a quella package-installche installa i pacchetti solo se non sono già installati, altrimenti inizializza semplicemente i pacchetti.


5

Uso un piccolo trucco "rubato" da emacs-starter-kit (credo):

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

Quindi, quando ho bisogno di un pacchetto, uso semplicemente:

(maybe-install-and-require 'magit)

Sulle startup emacs, valutando la mia configurazione, package.elfornirà l'installazione di magit se non è installato.

Puoi trovare la mia configurazione qui:

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


1
Seguendo la stessa filosofia, potresti usare 'paradosso-richiedere' dal paradosso
csantosb,

3

Ho ~ / emacs directory che è controllata dalla versione mercuriale e contiene tutto ciò che la mia configurazione di emacs è composta da (~ / emacs / site-lisp per le librerie scaricate manualmente, ~ / emacs / elpa per le librerie installate da elpa, ~ / emacs / etc / per .emacs diviso, ~ / emacs / dot-emacs.el che simbolizzo come ~ / .emacs). Richiedeva una modifica di alcuni pacchetti per avere tutti i file importanti all'interno di questo albero, ma funziona bene. Quei pochi bit che sono specifici della macchina che ho implementato dai condizionali sul nome del sistema.

Quindi, dopo aver installato / riconfigurato / modificato qualsiasi cosa, commetto semplicemente pull / push tutte le modifiche tra tutte le macchine che utilizzo.

Un ulteriore vantaggio è che ho una cronologia completa della mia configurazione e posso tornare indietro / bisecare / ripristinare nel caso in cui qualcosa vada storto.

PS mercurial sembra particolarmente adatto in quanto ha un pull / push naturale a due lati, ma una configurazione simile non dovrebbe essere difficile da ottenere con git o qualsiasi altro dvcs.


3

Ho questo setup-packages.elnella mia configurazione di emacs che è un ibrido di codice dal blog di Prelude e Tomorokoshi sulla gestione dei pacchetti .

setup-packages.el fa quanto segue:

  • Creare una directory per i elpapacchetti se non ne esiste una e aggiungerla al suo sottodirectory load-path.
  • package-archivesElenco di aggiornamento con Melpa.
  • Controlla se hai tutti i pacchetti elencati my-packagesnell'elenco installato. Se un pacchetto non è installato, installarlo.

Come implementare

  • Salva quanto setup-packages.elsotto nella tua ~/.emacs.d/directory.
  • Set user-emacs-directory, setup-packages-filee my-packagesle variabili nel vostro init.ele fare (load setup-packages-file).

Quando avvii emacs per la prima volta su un computer su cui non sono installati questi pacchetti, tutti i pacakge elencati my-packagesverranno installati 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

Avresti bisogno di quanto segue nel tuo 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

Per rispecchiare la mia configurazione ho deciso di optare per un approccio diverso, usando Syncthing ; ogni cambiamento in uno qualsiasi dei miei file di configurazione si propaga a qualsiasi altro dei miei pc senza preoccuparsene, quindi quando aggiorno i pacchetti devo farlo solo in uno dei pc.


2

RSYNC : sincronizza selezionare cartelle / file utilizzando rsync, su una rete domestica o tramite sshun server remoto.

rsyncè un'utilità di sincronizzazione unidirezionale che è in grado di eliminare i file sulla destinazione, quindi assicurati di eseguire il backup dei dati su entrambe le posizioni di origine e destinazione e di testare accuratamente utilizzando l' --dry-runopzione, prima di fare la cosa reale.

Per informazioni su come configurare correttamente il .authinfofile, consultare https://www.gnu.org/software/emacs/manual/auth.html Il.authinfo contenuto di un file di esempio (che può contenere più voci diverse) è il seguente:

machine mymachine login myloginname password mypassword port myport

Configurare e utilizzare la funzione rsync-remoteper la sincronizzazione sshcon un server remoto. In alternativa, utilizzare la funzione rsync-localper sincronizzare sullo stesso computer o su una rete domestica attendibile.

(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 crea un repository locale di tutti i pacchetti installati.

L'utilizzo è semplice, basta eseguire M-x elpamr-create-mirror-for-installed.

Su altre macchine, inserisci (setq package-archives '(("myelpa" . "~/myelpa/")))nel tuo .emacse riavvia Emacs.

Ora su tutte le macchine, ottieni esattamente la stessa versione dei pacchetti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.