Synchroniser les paquets entre différentes machines

57

J'utilise emacs à différents endroits et je souhaite disposer d'une configuration et de packages similaires installés partout. Je suppose que je peux utiliser un référentiel de contrôle de version pour les fichiers d'installation. Depuis que j'utilise Prelude , ce serait ~/.emacs.d/personal/.

Je ne sais pas comment faire avec les paquets. Existe-t-il un fichier quelque part dans .emacs.d/la liste des packages installés que je peux utiliser pour créer emacs sur d’autres machines et pour installer également ceux répertoriés ici?

El Diego Efe
la source
2
Pour moi, les packages installés ne sont qu'une partie de mon référentiel de contrôle de version Emacs. Je compile en octets les paquetages avec la version la plus ancienne d’Emacs que je veux / dois utiliser et mets également les fichiers .elc dans le référentiel. Cela offre un maximum de contrôle et de cohérence. Le compromis est la taille (relativement) grande du référentiel. Mon dépôt git de 6 ans a une taille de 120 Mo. Bien que je puisse probablement m'en tirer avec un dixième de la somme si je n'incluais pas les paquets, ces quelques mégaoctets "gaspillés" ne m'inquiètent vraiment pas.
paprika
1
Je devrais ajouter que bien qu'ELPA / MELPA / ... soit très populaire de nos jours, tous les packages ne sont toujours pas disponibles par leur intermédiaire. Donc, si vous utilisez un paquet nécessitant une installation manuelle, vous ne voudrez peut-être pas répliquer l'effort sur chaque nouvel ordinateur que vous utilisez (plus pour chaque mise à niveau du paquet). Là encore, une solution simple consiste à ajouter le package à votre référentiel de contrôle de version Emacs.
paprika
Ne pouvez-vous pas simplement compiler un octet sur le site et vous en sortir en ignorant les fichiers .elc dans git?
Vamsi
@Vamsi: J'ai mis les fichiers compilés en octets dans le référentiel car certains packages (non-ELPA) sont un peu difficiles à compiler en raison de leurs dépendances. Pour ceux-ci, je n'aime pas répéter le processus si ce n'est nécessaire.
paprika
@paprika Pourriez-vous fournir quelques détails sur la manière d'obtenir votre configuration? Est-ce que vous ajoutez tout dans .emacs.d plus .emacs au référentiel et c'est tout?
Débutant le

Réponses:

50

Il n'y a pas de fichier manifeste généré automatiquement que vous pouvez synchroniser pour obtenir l'effet souhaité.

Cela dit, vous pouvez ajouter des appels à package-installvotre configuration emacs elle-même.

(package-install 'auctex)

L'idée est que package-installc'est idempotent, donc si le paquet est déjà présent, rien ne se passera réellement. En supposant que vous ayez un tel appel pour chaque paquet que vous utilisez (ou au moins les feuilles du graphique de dépendance), cela synchroniserait efficacement vos paquets sur toutes les machines.


Pour plusieurs packages, vous pouvez utiliser les éléments suivants:

(setq my-package-list '(package1 package2 packageN))
(mapc #'package-install my-package-list)
Sigma
la source
2
Je suis surpris de voir à quel point cette approche est simplifiée par rapport à la solution proposée ici . Y a-t-il une différence? L'extrait utilise également package-install.
Stenskjaer
1
Il y a eu des changements package.eldepuis cette réponse liée. Il est possible qu’à l’époque des package-installopérations aient été effectuées sur des packages existants, pas seulement désinstallés.
Jonathan Leech-Pepin
3
Cette technique est malheureusement problématique, même si le référentiel d'archives de paquetages est uniforme entre les machines et spécifié dans SCM. Cela ne garantit pas que les versions des packages sont identiques entre les machines. Le problème est que les versions de paquet ne sont pas spécifiées; ces paquets individuels peuvent diverger avec le temps et leurs dépendances peuvent devenir incompatibles. Cela peut se produire assez facilement sur des archives de paquetages actifs telles que melpa.
ctpenrose
@ctpenrose: Avez-vous une suggestion pour éviter ce problème?
étudiant
@student J'ai minimisé le problème en utilisant melpa-stable et en mettant à jour les paquets moins souvent.
ctpenrose
34

Je garde mon répertoire .emacs.d dans le contrôle de version. Ensuite, dans mon init.el et les fichiers suivants, j'utilise use-package pour définir la configuration du paquet. Use-package ne charge pas uniquement vos paquets, il les téléchargera à la demande s'ils n'existent pas dans les dépôts de paquets que vous avez configurés.

Par exemple, j'utilise le mode go, mais pas sur toutes les machines. Dans mon init.el j'ai les éléments suivants:

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

Cela ajoute un crochet de mode, mais plus important encore, en spécifiant :ensure t, le package sera téléchargé à la demande.

Pour garder une machine synchronisée, vous pouvez simplement effectuer une extraction ou une extraction dans le dépôt et démarrer Emacs. Tous les nouveaux packages seront téléchargés et installés.

Elarson
la source
C’est la solution que j’utilise maintenant aussi, plutôt que Cask, principalement parce que (comme le notait T. Verron), Cask ne fonctionne pas (bien) sous Windows, et constitue une autre dépendance.
Andy
1
C’est ce que j’utilise aussi, mais au lieu de :ensure go-moderépéter le nom du paquet, vous pouvez simplement spécifier:ensure t
Pedro Luz
Bon point! C'est une vieille réponse. Je vais le mettre à jour.
Elarson
Vous pouvez également utiliser le :hookmot clé pour simplifier votre code.
Guilherme Salomé
17

Dans Emacs-25, il y a la variable package-selected-packages, vous pouvez donc personnaliser cette variable et l'utiliser package-install-selected-packagespour vous assurer qu'elle est installée.

Stefan
la source
Notez que je le vois, le nom de cette commande est un peu déroutant. Puis-je le changer en package-install-selected-packages?
Malabarba
En supposant que vous vouliez dire "Maintenant" au lieu de "Note", oui.
Stefan
9

Ce que vous voulez utiliser est Cask , qui vous permet de créer un fichier Cask spécifiant sur quels paquets installer cask install. Il peut être utilisé pour gérer facilement les dépendances d’un paquet et les "dépendances" de votre configuration Emacs. Placez votre fichier Cask sous contrôle de version et installez / mettez à jour des paquetages machine par machine.

Andy
la source
4
Il est à noter que (à ce jour) cette solution ne fonctionne pas pour les machines Windows.
T. Verron
1
Je n'utilise plus cette solution, pour votre raison même (et que Cask est encore une autre dépendance). Idéal pour faire des paquets; horrible pour la gestion de la configuration.
Andy
6

Une autre approche serait la suivante: puisque je ne veux pas seulement synchroniser mes paquets Emacs, mais aussi d' autres fichiers (par exemple .emacs, .bashrcmais aussi d' autres répertoires) entre mon serveur et mon ordinateur portable, j'ai commencé à utiliser unison, pour synchroniser les fichiers et des répertoires. Ainsi, lorsque je travaille sur mon ordinateur portable, je cours simplement unison laptopavant toute autre chose. Mon ~/.unison/laptop.prffichier contient la section suivante pour les fichiers liés à Emacs:

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

Étant donné que mes paquets Emacs (ainsi que mes sauvegardes et signets Emacs) sont stockés dans ~/.emacs.dcette mémoire, cela signifie que je dispose de tout le matériel sur toutes mes machines.

Une autre approche serait de placer le .emacs.drépertoire dans un répertoire synchronisé avec OwnCloud, DropBox ou tout autre service de synchronisation de fichiers, puis de créer des liens symboliques à partir ~/.emacs.dde ce répertoire partagé.

ph0t0nix
la source
5

Bien que ce package.elsoit la méthode standard pour installer des paquetages, vous pouvez aussi essayer el-getce qui est très utile pour installer des paquets qui ne sont pas (ou ne peuvent pas être) sur elpa. Cette réponse traite de la synchronisation de tels paquets.

La façon dont vous vous assurez que les paquetages sont installés lors de l’utilisation de el-get est d’ajouter quelque chose comme ceci à votre fichier init

(el-get 'sync '(packages))

où packages correspond à la liste des packages à installer. Cette fonction est similaire au fait package-installqu’elle installe les packages uniquement s’ils ne sont pas déjà installés, sinon elle initialise simplement les packages.

Iqbal Ansari
la source
5

J'utilise un petit truc "volé" dans emacs-starter-kit (je pense):

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

Alors, quand j'ai besoin d'un paquet, j'utilise simplement:

(maybe-install-and-require 'magit)

Sur les startups emacs, en évaluant ma configuration, il package.elsera possible d’installer magit s’il n’est pas installé.

Vous pouvez trouver ma configuration ici:

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

Mdallastella
la source
1
En suivant la même philosophie, vous pourriez utiliser paradox-require du paradoxe
csantosb
3

J'ai le répertoire ~ / emacs qui est contrôlé par la version mercurial et contient tout ce que ma configuration emacs contient (~ / emacs / site-lisp pour les bibliothèques téléchargées manuellement, ~ / emacs / elpa pour les bibliothèques installées par elpa, ~ / emacs / etc / pour les .emacs fractionnés, ~ / emacs / dot-emacs.el que je symlink comme ~ / .emacs). Certains paquets ont dû être peaufinés pour avoir tous les fichiers importants dans cet arbre, mais cela fonctionne bien. J'ai implémenté ces quelques bits spécifiques à la machine par des conditions sur le nom du système.

Donc, après avoir installé / reconfiguré / modifié quelque chose, je commets simplement à tirer / pousser tous les changements entre toutes les machines que j'utilise.

L’avantage supplémentaire est que j’ai un historique complet de ma configuration et que je peux revenir en arrière / bisecter / revenir en cas de problème.

PS mercurial semble particulièrement adapté car il possède une traction / poussée naturelle des deux côtés, mais une configuration similaire ne devrait pas être difficile à obtenir avec git ou tout autre lecteur de DVD.

Mekk
la source
3

J'ai ceci setup-packages.eldans ma configuration emacs, qui est un hybride de code provenant de Prelude et du blog de Tomorokoshi sur la gestion des paquets .

setup-packages.el fait ce qui suit:

  • Créez un répertoire pour les elpapackages s’il n’en existe pas et ajoutez-le ainsi que ses sous-répertoires au répertoire load-path.
  • Mettre à jour la package-archivesliste avec Melpa.
  • Vérifiez si tous les packages répertoriés dans la my-packagesliste sont installés. Si un paquet n'est pas installé, installez-le.

Comment mettre en œuvre

  • Enregistrez le setup-packages.elci - dessous dans votre ~/.emacs.d/répertoire.
  • Définir user-emacs-directory, setup-packages-fileet les my-packagesvariables dans votre init.elet faire (load setup-packages-file).

Lorsque vous démarrez emacs pour la première fois sur un ordinateur sur lequel ces packages ne sont pas installés, tous les packages énumérés dans my-packagess’installent automatiquement.

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

Vous auriez besoin des éléments suivants dans votre 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
la source
2

Afin de refléter ma configuration, j'ai décidé d'adopter une approche différente en utilisant Syncthing ; chaque modification de l'un de mes fichiers de configuration se propage à un autre de mes ordinateurs sans avoir à s'en soucier. Ainsi, lorsque je mets à jour des paquets, je ne dois le faire que dans l'un des ordinateurs.

Csantosb
la source
2

RSYNC : Synchronisez certains dossiers / fichiers à l’aide d’ rsyncun réseau domestique ou sshd’un serveur distant.

rsyncest un utilitaire de synchronisation unidirectionnel capable de supprimer des fichiers sur la cible. Veillez donc à sauvegarder vos données sur les emplacements source et cible et à effectuer des tests approfondis à l'aide de l' --dry-runoption, avant de procéder.

Pour savoir comment configurer correctement le .authinfofichier, voir https://www.gnu.org/software/emacs/manual/auth.html. Un exemple de .authinfocontenu de fichier (pouvant contenir plusieurs entrées différentes) est le suivant:

machine mymachine login myloginname password mypassword port myport

Configurez et utilisez la fonction rsync-remotepour effectuer une synchronisation sur sshun serveur distant. Ou utilisez la fonction rsync-localpour synchroniser sur le même ordinateur ou sur un réseau domestique sécurisé.

(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!"))))))
liste de loi
la source
0

https://github.com/redguardtoo/elpa-mirror crée un référentiel local de tous les packages installés.

L'utilisation est simple, il suffit de courir M-x elpamr-create-mirror-for-installed.

Sur d’autres machines, insérez-les (setq package-archives '(("myelpa" . "~/myelpa/")))dans .emacset redémarrez Emacs.

Maintenant, sur toutes les machines, vous obtenez exactement la même version des packages.

chen bin
la source