¿Cómo creo un DMG atractivo para Mac OS X usando herramientas de línea de comandos?


212

Necesito crear un buen instalador para una aplicación de Mac. Quiero que sea una imagen de disco (DMG), con un tamaño, diseño e imagen de fondo predefinidos.

Necesito hacer esto mediante programación en un script, para integrarme en un sistema de compilación existente (más de un sistema de paquetes realmente, ya que solo crea instaladores. Las compilaciones se realizan por separado).

Ya tengo la creación de DMG usando "hdiutil", lo que aún no he descubierto es cómo hacer un diseño de icono y especificar un mapa de bits de fondo.


1
¿No es esto algo para Ask Different?
Lenar Hoyt

Respuestas:


199

Después de mucha investigación, se me ocurrió esta respuesta, y la pongo aquí como respuesta a mi propia pregunta, como referencia:

  1. Asegúrese de que "Habilitar acceso para dispositivos de asistencia" esté marcado en Preferencias del sistema >> Acceso universal. Se requiere que AppleScript funcione. Es posible que deba reiniciar después de este cambio (de lo contrario no funciona en Mac OS X Server 10.4).

  2. Crea un DMG R / W. Debe ser más grande de lo que será el resultado. En este ejemplo, la variable bash "tamaño" contiene el tamaño en Kb y el contenido de la carpeta en la variable bash "fuente" se copiará en el DMG:

    hdiutil create -srcfolder "${source}" -volname "${title}" -fs HFS+ \
          -fsargs "-c c=64,a=16,e=16" -format UDRW -size ${size}k pack.temp.dmg
    
  3. Monte la imagen del disco y almacene el nombre del dispositivo (es posible que desee usar la suspensión durante unos segundos después de esta operación):

    device=$(hdiutil attach -readwrite -noverify -noautoopen "pack.temp.dmg" | \
             egrep '^/dev/' | sed 1q | awk '{print $1}')
    
  4. Almacene la imagen de fondo (en formato PNG) en una carpeta llamada ".background" en el DMG, y almacene su nombre en la variable "backgroundPictureName".

  5. Use AppleScript para establecer los estilos visuales (el nombre de .app debe estar en la variable bash "applicationName", use variables para las otras propiedades según sea necesario):

    echo '
       tell application "Finder"
         tell disk "'${title}'"
               open
               set current view of container window to icon view
               set toolbar visible of container window to false
               set statusbar visible of container window to false
               set the bounds of container window to {400, 100, 885, 430}
               set theViewOptions to the icon view options of container window
               set arrangement of theViewOptions to not arranged
               set icon size of theViewOptions to 72
               set background picture of theViewOptions to file ".background:'${backgroundPictureName}'"
               make new alias file at container window to POSIX file "/Applications" with properties {name:"Applications"}
               set position of item "'${applicationName}'" of container window to {100, 100}
               set position of item "Applications" of container window to {375, 100}
               update without registering applications
               delay 5
               close
         end tell
       end tell
    ' | osascript
    
  6. Finalice el DMG configurando los permisos correctamente, comprimiéndolo y soltándolo:

    chmod -Rf go-w /Volumes/"${title}"
    sync
    sync
    hdiutil detach ${device}
    hdiutil convert "/pack.temp.dmg" -format UDZO -imagekey zlib-level=9 -o "${finalDMGName}"
    rm -f /pack.temp.dmg 
    

En Snow Leopard, el applecript anterior no establecerá la posición del icono correctamente; parece ser un error de Snow Leopard. Una solución es simplemente llamar a cerrar / abrir después de configurar los iconos, es decir:

..
set position of item "'${applicationName}'" of container window to {100, 100}
set position of item "Applications" of container window to {375, 100}
close
open

1
Excelente. Dos preguntas al respecto: 1. Recomienda sleepdespués de montar la imagen. ¿Cuánto tiempo? ¿No es posible esperar determinísticamente la finalización del proceso? Lo mismo para delay 5en tu AppleScript. Siempre desconfío de tiempos de espera tan arbitrarios, habiendo tenido una muy mala experiencia con ellos. 2. En tu paso 6 llamas syncdos veces, ¿por qué?
Konrad Rudolph el

No he encontrado ninguna forma de esperar determinísticamente la finalización del comando 'actualizar sin registrar aplicaciones'. No estoy seguro de que sea necesario dormir después de "hdiutil attach", tendrá que verificar la documentación (man hdiutil). La sincronización solo debería ser necesaria para que se llame una vez, lo hago dos veces por viejo hábito desde los viejos tiempos de SunOS.
Ludvig A. Norin

1
Permite que AppleScript simule la entrada del mouse y del teclado y muchas otras cosas.
Ludvig A. Norin

1
Si el script se cuelga hasta que se agota el tiempo en la línea "actualizar sin registrar aplicaciones", entonces el paso 1 no se ha realizado (o se ha deshecho). Acabo de descubrir esto de la manera difícil.
Jay Maynard K5ZC

Creo que encontré una pequeña falla: hay una expulsión en AppleScript, que se realiza antes de intentar ejecutar chmod. chmod fallará porque ahora se expulsa el disco.
Trejkaz

56

Hay un pequeño script de Bash llamado create-dmg que crea DMG sofisticados con fondos personalizados, posicionamiento de iconos personalizado y nombre de volumen.

Lo construí hace muchos años para la compañía que dirigía en ese momento; sobrevive de la contribución de otras personas desde entonces y, según los informes, funciona bien.

También hay node-appdmg que parece un esfuerzo más moderno y activo basado en Node.js; échale un vistazo también.


77
El enlace a su script bash está roto. ¿Puedes ponerlo en una idea básica en github.com? Gracias
dquimper

create-dmg NO posiciona bien el ícono. Si necesita esta función, no la use.
laike9m

1
@ laike9m Han pasado más de 5 años desde la última vez que lo toqué. Realmente ya no considero que los DMG sean la mejor manera de distribuir el software de Mac. Por lo tanto, sobrevive gracias a las contribuciones de otros, ojalá alguien descubra el problema de los íconos también.
Andrey Tarantsov

3
@Zmey Bueno, como usuario, prefiero los archivos zip con diferencia. Puntos de bonificación para la copia automática /Applicationssi la aplicación detecta que se está ejecutando ~/Downloads. Vea también este viejo artículo de John Gruber .
Andrey Tarantsov


37

No vayas ahí. Como desarrollador de Mac a largo plazo, puedo asegurarle que ninguna solución funciona realmente bien. Intenté muchas soluciones, pero no todas son demasiado buenas. Creo que el problema es que Apple realmente no documenta el formato de metadatos para los datos necesarios.

Así es como lo estoy haciendo durante mucho tiempo, con mucho éxito:

  1. Cree un nuevo DMG, que se pueda escribir (!), Lo suficientemente grande como para contener los archivos binarios y adicionales esperados, como el archivo Léame (puede que sea escaso).

  2. Monte el DMG y dele un diseño manualmente en Finder o con las herramientas que le convengan para hacerlo (consulte el enlace FileStorm en la parte inferior para obtener una buena herramienta). La imagen de fondo suele ser una imagen que colocamos en una carpeta oculta (".algo") en el DMG. Ponga una copia de su aplicación allí (cualquier versión, incluso una obsoleta lo hará) Copie otros archivos (alias, léame, etc.) que desee allí, nuevamente, las versiones obsoletas funcionarán bien. Asegúrese de que los íconos tengan los tamaños y las posiciones correctas (IOW, coloque el DMG de la manera que desee).

  3. Desmontar el DMG nuevamente, todas las configuraciones deben estar almacenadas por ahora.

  4. Escriba un script de creación de DMG que funcione de la siguiente manera:

    • Copia el DMG, por lo que el original nunca más se toca.
    • Monta la copia.
    • Reemplaza todos los archivos con los más actualizados (por ejemplo, la última aplicación después de la compilación). Simplemente puede usar mv o ditto para eso en la línea de comandos. Tenga en cuenta que cuando reemplaza un archivo como ese, el ícono permanecerá igual, la posición permanecerá igual, todo menos el contenido del archivo (o directorio) permanece igual (al menos con lo mismo, que usualmente usamos para esa tarea) . Por supuesto, también puede reemplazar la imagen de fondo por otra (solo asegúrese de que tenga las mismas dimensiones).
    • Después de reemplazar los archivos, vuelva a desmontar el script de la copia DMG.
    • Finalmente, llame a hdiutil para convertir la escritura, a un DMG comprimido (y no escribible).

Puede que este método no suene óptimo, pero confía en mí, funciona muy bien en la práctica. Puede poner el DMG original (plantilla DMG) incluso bajo el control de versión (por ejemplo, SVN), por lo que si alguna vez lo cambia / destruye accidentalmente, puede volver a una revisión donde todavía estaba bien. Puede agregar la plantilla DMG a su proyecto Xcode, junto con todos los demás archivos que pertenecen al DMG (archivo Léame, archivo URL, imagen de fondo), todo bajo control de versión y luego crear un objetivo (por ejemplo, un objetivo externo llamado "Crear DMG") y allí ejecuta el script DMG de arriba y agrega tu antiguo objetivo principal como objetivo dependiente. Puede acceder a los archivos en el árbol de Xcode usando $ {SRCROOT} en el script (siempre es la raíz fuente de su producto) y puede acceder a los productos de compilación usando $ {BUILT_PRODUCTS_DIR} (es siempre el directorio donde Xcode crea los resultados de la compilación) .

Resultado: en realidad, Xcode puede producir el DMG al final de la compilación. Un DMG que está listo para lanzar. De este modo, no solo puede crear un DMG relase bastante fácil, sino que también puede hacerlo en un proceso automatizado (en un servidor sin cabeza si lo desea), utilizando xcodebuild desde la línea de comandos (compilaciones nocturnas automatizadas, por ejemplo).

En cuanto al diseño inicial de la plantilla, FileStorm es una buena herramienta para hacerlo. Es comercial, pero muy potente y fácil de usar. La versión normal cuesta menos de $ 20, por lo que es realmente asequible. Tal vez uno pueda automatizar FileStorm para crear un DMG (por ejemplo, a través de AppleScript), nunca lo intentó, pero una vez que haya encontrado el DMG de plantilla perfecto, es realmente fácil actualizarlo para cada versión.


3
Ya he descartado la idea de hacerlo de esta manera, por varias razones. Aquí hay dos de ellos: el contenido de los instaladores variará según el producto, y queremos confiar solo en el software instalado en las máquinas de empaque y los scripts, una rutina manual única y mínima para agregar nuevos productos.
Ludvig A. Norin

Este es el mismo escenario que tenemos nosotros. Tenemos más de una docena de productos; cada uno tiene un DMG completamente diferente. Crear una plantilla DMG por producto es una tarea única y te lleva un par de minutos. ¿Y qué quieres decir con "instalador"? Paquetes de instalación PKG / MPKG?
Mecki

No es el mismo escenario. Añadimos productos a menudo, con poca antelación. La rutina manual mínima significa ejecutar un script que le da al producto un nombre y algunos otros atributos. Hay razones más allá de esto también que nos hicieron tomar la decisión de no usar ese tipo de solución.
Ludvig A. Norin

Hemos separado el proceso del paquete del proceso de compilación, porque lo realizan diferentes personas en diferentes momentos. El proceso del paquete crea instaladores para Windows, Windows CE, Symbian, AIX, Linux y Solaris también.
Ludvig A. Norin

Probablemente se esté refiriendo a hdiutil, no a hdutil.
Ivan Vučica

25

Actualizando esta pregunta proporcionando esta respuesta.

appdmges un programa de línea de comandos simple, fácil de usar y de código abierto que crea archivos dmg a partir de una especificación json simple. Eche un vistazo al archivo Léame en el sitio web oficial:

https://github.com/LinusU/node-appdmg

Ejemplo rápido:

  1. Instalar appdmg

    npm install -g appdmg
    
  2. Escribir un archivo json ( spec.json)

    {
      "title": "Test Title",
      "background": "background.png",
      "icon-size": 80,
      "contents": [
        { "x": 192, "y": 344, "type": "file", "path": "TestApp.app" },
        { "x": 448, "y": 344, "type": "link", "path": "/Applications" }
      ]
    }
    
  3. Ejecute el programa

    appdmg spec.json test.dmg
    

(descargo de responsabilidad. Soy el creador de appdmg)


3
Simple y efectivo. El único inconveniente es que requiere la instalación de npm
Jack James

@ Creator: ¿puedes hacer que avance como esta oferta GUI sin tener que arrastrar y soltar? s.sudre.free.fr/Software/Packages/about.html

Si el archivo .app no ​​está en la misma carpeta que el archivo json, esto no funciona, da un error de "archivo no encontrado" cuando especifica una ruta relativa en "ruta"
Joey

@Joey, ¿podría abrir un problema en el repositorio de Github si no funciona para usted?
Linus Unnebäck

@Joey: No es necesario estar en la misma carpeta, tengo antecedentes, la aplicación está en una ruta diferente, solo paso las rutas relativas como ../../../ABC
Anoop Vaidya

22

Para aquellos de ustedes que estén interesados ​​en este tema, debo mencionar cómo creo el DMG:

hdiutil create XXX.dmg -volname "YYY" -fs HFS+ -srcfolder "ZZZ"

dónde

XXX == disk image file name (duh!)
YYY == window title displayed when DMG is opened
ZZZ == Path to a folder containing the files that will be copied into the DMG

1
Está bien, pero no aborda la necesidad real (imagen de fondo, posicionamiento de elementos en la carpeta, etc.)
Tom Bogle

creó DMG pero nuevamente tengo que ejecutar mi Script (.sh) usando CMD, necesito que ejecute automáticamente sh después de crear DMG
SANTOSH

13

Mi aplicación, DropDMG , es una manera fácil de crear imágenes de disco con imágenes de fondo, diseños de iconos, iconos de volumen personalizados y acuerdos de licencia de software. Se puede controlar desde un sistema de compilación a través de la herramienta de línea de comandos "dropdmg" o AppleScript. Si lo desea, los archivos RTF de imagen y licencia se pueden almacenar en su sistema de control de versiones.


3
Mi equipo hace que esto funcione de manera hermosa y automática en un servidor de compilación Jenkins CI, completo con fondo, arrastre al alias de Aplicaciones. Corre, no camines, a DropDMG para crear imágenes de disco.
Paul Collins

Buena aplicación, la compraré después de que caduque mi versión de prueba.
kilik52 02 de

Se ve bien, pero no parece tener la opción de cambiar el tamaño de la ventana.
avi

@avi DropDMG ajusta automáticamente el tamaño de la ventana a la imagen de fondo que configuró (o inserte en esa imagen si lo desea).
Michael Tsai

¡Muchas gracias! ¿Es posible cambiar el tamaño sin imagen de fondo?
avi


5

Para crear un buen DMG, ahora puede usar algunas fuentes abiertas bien escritas:


Podría ser que lo movieron. Puede usar create-dmg y node-appdmg. Estoy usando create-dmg. Es bueno.
Anni S

@ PamelaCook-LightBeCorp En caso de que sigas interesado. El enlace a dmgbuild ha sido corregido.
Subóptimo

4

Si desea establecer un icono de volumen personalizado, utilice el siguiente comando

/*Add a drive icon*/
cp "/Volumes/customIcon.icns" "/Volumes/dmgName/.VolumeIcon.icns"  


/*SetFile -c icnC will change the creator of the file to icnC*/
SetFile -c icnC /<your path>/.VolumeIcon.icns

Ahora crea lectura / escritura dmg

/*to set custom icon attribute*/
SetFile -a C /Volumes/dmgName

¿Puedes explicar qué significa "tu camino" aquí? ¿Puedo usar cualquier archivo de íconos en el disco y SetFile lo copiaría, o necesitaría usar un archivo que esté dentro de .dmg? Solo tengo una Mac, por lo que es difícil probar si las cosas funcionarán en otras máquinas.
Milan Babuškov

"tu camino" es el nombre DMG. (/ Volumes / dmgName)
Parag Bafna

Debes copiar el archivo icns. cp "/Volumes/customIcon.icns" "/Volumes/dmgName/.VolumeIcon.icns"
Parag Bafna

4

Finalmente conseguí que esto funcionara en mi propio proyecto (que está en Xcode). Agregar estos 3 scripts a su fase de compilación creará automáticamente una imagen de disco para su producto que sea agradable y ordenada. Todo lo que tiene que hacer es crear su proyecto y el DMG lo estará esperando en la carpeta de sus productos.

Script 1 (Crear imagen de disco temporal):

#!/bin/bash
#Create a R/W DMG

dir="$TEMP_FILES_DIR/disk"
dmg="$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.temp.dmg"

rm -rf "$dir"
mkdir "$dir"
cp -R "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app" "$dir"
ln -s "/Applications" "$dir/Applications"
mkdir "$dir/.background"
cp "$PROJECT_DIR/$PROJECT_NAME/some_image.png" "$dir/.background"
rm -f "$dmg"
hdiutil create "$dmg" -srcfolder "$dir" -volname "$PRODUCT_NAME" -format UDRW

#Mount the disk image, and store the device name
hdiutil attach "$dmg" -noverify -noautoopen -readwrite

Script 2 (Establecer script de propiedades de ventana):

#!/usr/bin/osascript
#get the dimensions of the main window using a bash script

set {width, height, scale} to words of (do shell script "system_profiler SPDisplaysDataType | awk '/Main Display: Yes/{found=1} /Resolution/{width=$2; height=$4} /Retina/{scale=($2 == \"Yes\" ? 2 : 1)} /^ {8}[^ ]+/{if(found) {exit}; scale=1} END{printf \"%d %d %d\\n\", width, height, scale}'")
set x to ((width / 2) / scale)
set y to ((height / 2) / scale)

#get the product name using a bash script
set {product_name} to words of (do shell script "printf \"%s\", $PRODUCT_NAME")
set background to alias ("Volumes:"&product_name&":.background:some_image.png")

tell application "Finder"
    tell disk product_name
        open
        set current view of container window to icon view
        set toolbar visible of container window to false
        set statusbar visible of container window to false
        set the bounds of container window to {x, y, (x + 479), (y + 383)}
        set theViewOptions to the icon view options of container window
        set arrangement of theViewOptions to not arranged
        set icon size of theViewOptions to 128
        set background picture of theViewOptions to background
        set position of item (product_name & ".app") of container window to {100, 225}
        set position of item "Applications" of container window to {375, 225}
        update without registering applications
        close
    end tell
end tell

La medida anterior para la ventana funciona para mi proyecto específicamente debido al tamaño de la imagen de fondo y la resolución del icono; Es posible que deba modificar estos valores para su propio proyecto.

Script 3 (Crear script de imagen de disco final):

#!/bin/bash
dir="$TEMP_FILES_DIR/disk"
cp "$PROJECT_DIR/$PROJECT_NAME/some_other_image.png" "$dir/"

#unmount the temp image file, then convert it to final image file
sync
sync
hdiutil detach /Volumes/$PRODUCT_NAME
rm -f "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.dmg"
hdiutil convert "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.temp.dmg" -format UDZO -imagekey zlib-level=9 -o "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.dmg"
rm -f "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.temp.dmg"

#Change the icon of the image file
sips -i "$dir/some_other_image.png"
DeRez -only icns "$dir/some_other_image.png" > "$dir/tmpicns.rsrc"
Rez -append "$dir/tmpicns.rsrc" -o "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.dmg"
SetFile -a C "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.dmg"

rm -rf "$dir"

¡Asegúrese de que los archivos de imagen que está utilizando estén en el directorio $ PROJECT_DIR / $ PROJECT_NAME /!


Está creando una imagen de disco en blanco en mi proyecto. Cualquier sugerencia.
ManiaChamp

La parte de script de Shell solo funciona, pero ¿cómo puedo agregar un script de Apple con Shell Script en RunScript?
ManiaChamp

2

Los archivos .DS_Store almacenan la configuración de Windows en Mac. La configuración de Windows incluye el diseño de los iconos, el fondo de la ventana, el tamaño de la ventana, etc. El archivo .DS_Store es necesario para crear la ventana para las imágenes montadas para preservar la disposición de los archivos y el fondo de las ventanas.

Una vez que haya creado el archivo .DS_Store, puede copiarlo en su instalador creado (DMG).


Pero primero agregue una imagen de fondo al dmg nombrándolo algo así como .background.png y luego use Cmd-J para agregar esa imagen (!) Al fondo.
Gijs

2

También necesito usar el enfoque de línea de comandos para hacer el empaque y la creación de dmg "programáticamente en un script". La mejor respuesta que encontré hasta ahora es el marco de construcción de lanzamiento del proyecto Adium '(Ver R1). Hay un script personalizado (AdiumApplescriptRunner) que le permite evitar la interacción de la GUI de Windows Server con OSX. El enfoque "osascript applescript.scpt" requiere que inicie sesión como generador y ejecute la creación de dmg desde una sesión de línea de comando vt100.

El sistema de gestión de paquetes OSX no es tan avanzado en comparación con otros Unixen que pueden realizar esta tarea de manera fácil y sistemática.

R1: http://hg.adium.im/adium-1.4/file/00d944a3ef16/Release


2

Acabo de escribir una nueva (amigable) línea de comando para hacer esto. No se basa en Finder / AppleScript, ni en ninguna de las API de Alias ​​Manager (en desuso), y es fácil de configurar y usar.

De todos modos, cualquiera que esté interesado puede encontrarlo en PyPi ; la documentación está disponible en Read The Docs .


1

Estas respuestas son demasiado complicadas y los tiempos han cambiado. Lo siguiente funciona bien en 10.9, los permisos son correctos y se ve bien.

Crear un DMG de solo lectura desde un directorio

#!/bin/sh
# create_dmg Frobulator Frobulator.dmg path/to/frobulator/dir [ 'Your Code Sign Identity' ]
set -e

VOLNAME="$1"
DMG="$2"
SRC_DIR="$3"
CODESIGN_IDENTITY="$4"

hdiutil create -srcfolder "$SRC_DIR" \
  -volname "$VOLNAME" \
  -fs HFS+ -fsargs "-c c=64,a=16,e=16" \
  -format UDZO -imagekey zlib-level=9 "$DMG"

if [ -n "$CODESIGN_IDENTITY" ]; then
  codesign -s "$CODESIGN_IDENTITY" -v "$DMG"
fi

Cree DMG de solo lectura con un icono (tipo .icns)

#!/bin/sh
# create_dmg_with_icon Frobulator Frobulator.dmg path/to/frobulator/dir path/to/someicon.icns [ 'Your Code Sign Identity' ]
set -e
VOLNAME="$1"
DMG="$2"
SRC_DIR="$3"
ICON_FILE="$4"
CODESIGN_IDENTITY="$5"

TMP_DMG="$(mktemp -u -t XXXXXXX)"
trap 'RESULT=$?; rm -f "$TMP_DMG"; exit $RESULT' INT QUIT TERM EXIT
hdiutil create -srcfolder "$SRC_DIR" -volname "$VOLNAME" -fs HFS+ \
               -fsargs "-c c=64,a=16,e=16" -format UDRW "$TMP_DMG"
TMP_DMG="${TMP_DMG}.dmg" # because OSX appends .dmg
DEVICE="$(hdiutil attach -readwrite -noautoopen "$TMP_DMG" | awk 'NR==1{print$1}')"
VOLUME="$(mount | grep "$DEVICE" | sed 's/^[^ ]* on //;s/ ([^)]*)$//')"
# start of DMG changes
cp "$ICON_FILE" "$VOLUME/.VolumeIcon.icns"
SetFile -c icnC "$VOLUME/.VolumeIcon.icns"
SetFile -a C "$VOLUME"
# end of DMG changes
hdiutil detach "$DEVICE"
hdiutil convert "$TMP_DMG" -format UDZO -imagekey zlib-level=9 -o "$DMG"
if [ -n "$CODESIGN_IDENTITY" ]; then
  codesign -s "$CODESIGN_IDENTITY" -v "$DMG"
fi

Si algo más tiene que suceder, lo más fácil es hacer una copia temporal de SRC_DIR y aplicar los cambios antes de crear un DMG.


Esta respuesta no agrega nada a lo que se ha escrito anteriormente, y tampoco responde a la pregunta original (no se trata solo de poner un ícono en el dmg, o cómo firmarlo; la pregunta se trata de agregar gráficos e íconos posicionados programáticamente para un dmg).
Ludvig A. Norin
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.