File: deb2docker.sh

package info (click to toggle)
devscripts 2.25.19
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 10,252 kB
  • sloc: perl: 27,139; sh: 11,887; python: 4,428; makefile: 382
file content (349 lines) | stat: -rwxr-xr-x 10,142 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
#! /bin/bash

# Purpose: install Debian packages in a Docker image
#
# Usage: deb2docker [options] packages
#   -B        do NOT build the image (default is to build)
#   -c CMD    command to run in the container (default to '/bin/bash')
#   -f FROM   indicate which distribution is to be used (default to debian:stable)
#   -h        show this help
#   -n NAME   name of the image (default to package list)
#   -o DIR    use given directory for the build (default in /tmp)
#   -p PRE    execute the given script during the container build (before packages install)
#   -s POST   execute the given script during the container build (after packages install)
#   -v        show package version
# The package list can be any Debian package, as well as local .deb
#
# Example: 'deb2docker -o /tmp/xeyes x11-apps' then '/tmp/xeyes/start xeyes'

# build:      docker build --rm Dockerfile
# run:        docker run   --rm -it NAME
# clean:      docker rmi NAME
# clean ALL:  docker system prune -a

# (c) E. Farhi - Synchrotron SOLEIL - GPL3

# requires: 
#   bash 
#   docker.io
# requires: docker privileges: 
#   sudo usermod -aG docker $USER

set -e

# default settings -------------------------------------------------------------
FROM=debian:stable
BUILD=1
NAME=
SETNAME=0
CMD="/bin/bash"
WORK_DIR=
SCRIPT=
PRE=
VERSION=1.0.8
ARGS="$@"

# handle input arguments -------------------------------------------------------
while getopts vhBf:n:c:o:d:p:s: flag
do
    case "${flag}" in
        h) # display help
        echo "Usage: $0 [-hB][-c CMD][-f FROM][-n NAME][-o DIR][-s SCRIPT] packages..."
        echo "  Build a Docker image with given Debian packages."
        echo "  Options:"
        echo "  -B        do NOT build the image (default is to build)"
        echo "  -c EXEC   command to run in the container (default to $CMD)"
        echo "  -f FROM   indicate which distribution is to be used (default to $FROM)"
        echo "  -h        show this help"
        echo "  -n NAME   name of the image (default to package list)"
        echo "  -o DIR    use given directory for the build (default is in /tmp)"
        echo "  -p PRE    execute script PRE before packages install"
        echo "  -s POST   execute script POST after packages install"
        echo "  -v        show package version"
        echo "  The package list can be any Debian package, as well as local .deb"
        echo " "
        echo "Example: '$0 -o /tmp/xeyes x11-apps' then '/tmp/xeyes/start xeyes'"
        exit 0; 
        ;;
        B) # do not build the image
        BUILD=0
        ;;
        f) # set FROM image
        FROM=${OPTARG}
        ;;
        n) # set image name
        NAME=${OPTARG}
        ;;
        c) # command to execute
        CMD=${OPTARG}
        ;;
        o|d) # output directory
        WORK_DIR=${OPTARG}
        ;;
        p) # PRE SCRIPT
        PRE=${OPTARG}
        ;;
        s) # SCRIPT (POST)
        SCRIPT=${OPTARG}
        ;;
        v) # VERSION
        echo "$0 version $VERSION"
        exit 0;
        ;;
        *)
        echo "ERROR: Invalid option. Use -h for help."
        exit 1;
        ;;
    esac
done
shift $((OPTIND-1))

# check that docker is installed
if ! command -v docker > /dev/null
then
    echo "ERROR: docker could not be found. Install it with: apt install docker.io"
    exit 1
fi

# set name when not set
if [ "x$NAME" = "x" ]; then
  SETNAME=1
fi

# create a temporary directory to work in --------------------------------------
if [ "x$WORK_DIR" = "x" ]; then
  N=`basename $0`
  WORK_DIR=`mktemp -p /tmp -d $N-XXXXXXXX`
else
  mkdir -p $WORK_DIR || echo "ERROR: Invalid directory $WORK_DIR"
fi
PW=$PWD


# search for executable commands -----------------
DEB=
# get a local copy of packages and find bin and desktop files
mkdir -p $WORK_DIR/apt      || exit # hold deb pkg copies for analysis

echo "$0: creating image $NAME in $WORK_DIR"
echo "Getting Debian packages..."
for i in $@; do
  echo "  $i"
  if [ -f "$i" ]; then
    cp $i $WORK_DIR/apt/
    n=`basename $i`
    DEB="$DEB /opt/install/$n"
  else
    DEB="$DEB $i"
    cd $WORK_DIR/apt
    apt download $i
    cd $PW
  fi
done


echo " "                                  >> $WORK_DIR/README
echo "Created with $0"                    >> $WORK_DIR/README
echo "$ARGS"                              >> $WORK_DIR/README
echo " "                                  >> $WORK_DIR/file_list.txt
for i in $WORK_DIR/apt/*.deb; do
  echo "Analyzing $i"
  
  N=$(dpkg-deb -f $i Package) || continue
  # set the container name if needed
  if [ "x$SETNAME" = "x2" ]; then
    NAME="$NAME-$N"
  fi
  if [ "x$SETNAME" = "x1" ]; then
    SETNAME=2
    NAME=$N
  fi
  echo "Package $N ------------------------------------------" >> $WORK_DIR/README
  dpkg-deb -I $i                                               >> $WORK_DIR/README
  
  F=`dpkg -c $i` 
  echo "$F" >> $WORK_DIR/file_list.txt
  echo " "  >> $WORK_DIR/README
  
done

# the base command to start the containers from image
cmd="docker run -it --net=host --env=DISPLAY --env='QT_X11_NO_MITSHM=1' --volume=\$HOME/.Xauthority:/home/user/.Xauthority:rw $NAME"

# prepare the Dockerfile -------------------------------------------------------
FILE="$WORK_DIR/Dockerfile"
DATE=`date`

if [ -f "$PRE" ]; then
  cp $PRE $WORK_DIR/
  N=`basename $PRE`
  PRE="RUN chmod a+x /opt/install/$N && sh -c \"/opt/install/$N\""
  PRE_FILE="ADD $N /opt/install/"
else
  PRE_FILE=
fi

if [ -f "$SCRIPT" ]; then
  cp $SCRIPT $WORK_DIR/
  N=`basename $SCRIPT`
  SCRIPT="RUN chmod a+x /opt/install/$N && sh -c \"/opt/install/$N\""
  SCRIPT_FILE="ADD $N /opt/install/"
else
  SCRIPT_FILE=
fi

echo "Creating Dockerfile $NAME into $FILE"
dd status=none of=${FILE} << EOF
# created by $0 on $DATE
# $ARGS
#
# Docker image $NAME
#
# build: docker build --rm Dockerfile
# run:   docker run --rm -it $NAME
# clean: docker rmi $NAME
# clean ALL: docker system prune -a

FROM $FROM

# copy required packages
ADD apt/  /opt/install/
ADD README /opt/install/
ADD file_list.txt /opt/install/
ADD Dockerfile /opt/install/
$PRE_FILE
$SCRIPT_FILE

# execute/install
$PRE
RUN apt-get update -y
RUN apt-get install -y --no-install-recommends bash xauth $DEB
$SCRIPT
RUN rm /opt/install/*.deb || echo " "

# start the container (interactive/terminal)
RUN useradd -ms /bin/bash user
ENV DISPLAY :0
USER user
CMD ["$CMD"]
EOF

# build docker -----------------------------------------------------------------
FILE=$WORK_DIR/build
dd status=none of=${FILE} << EOF
#!/bin/bash
# created by $0 on $DATE
# $ARGS
#
# build image $NAME
#
# Usage: build

docker build --rm -t $NAME .

# handle of Desktop launchers
mkdir -p launchers/
mkdir -p icons/

# get .desktop files ----------------------------------------------------------
D=\$(grep '\.desktop' file_list.txt) || echo "WARNING: No desktop files found."
# get the desktop files
D=\$(echo "\$D" | awk '{ print \$6 }')

# we need to copy them back, as well as their icons, and change the Exec lines
# create a container from image to access the files
id=\$(docker create $NAME)
for i in \$D; do
  docker cp \$id:\$i launchers/ || echo "WARNING: Failed to get desktop launcher \$i"
done

# get icon files --------------------------------------------------------------
D=\$(grep 'icon' file_list.txt) || echo "WARNING: No icon files found."
# get the icon files
D=\$(echo "\$D" | awk '{ print \$6 }')

# we need to copy them back, as well as their icons, and change the Exec lines
# create a container from image to access the files
id=\$(docker create $NAME)
for i in \$D; do
  docker cp \$id:\$i icons/ &> /dev/null|| echo "WARNING: Failed to get icon \$i"
done

# cleanup
docker rm -v \$id

# adapt the Desktop launchers to insert 'run', set Icons=
for i in launchers/*; do
  if [ ! -f "\$i" ]; then continue; fi
  I=\$(grep 'Icon=' \$i | cut -d = -f 2) || I=
  if [ ! -z "\$I" ]; then
    n=\`basename \$I\`
    if [ ! -f "icons/\$n" ]; then
      # get closest file that match Icon name when initial name is not present as a file
      n1=( icons/\$n* ) || n1=
      if [ ! -z "\$n1" ]; then
        n=\`basename \${n1[0]}\`
      fi
    fi
    sed -i "s|Icon=.*|Icon=icons/\$n|g" \$i   || echo " "
  fi
  I=\$(grep 'Exec=' \$i | cut -d = -f 2-) || I=
  sed -i "s|Exec=.*|Exec=sh -c \"echo '$NAME'; $cmd \$I\"|g" \$i                 || echo " "
  # make sure terminal is set (else 'docker -it' fails)
  I=\$(grep 'Terminal=' \$i | cut -d = -f 2) || I=
  if [ ! -z "\$I" ]; then
    sed -i 's|Terminal=.*|Terminal=true|g' \$i         || echo " "
  else
    echo "Terminal=true" >> \$i
  fi
  chmod a+x \$i                                         || echo " "
done

# create a Terminal launcher
echo "[Desktop Entry]"       > launchers/$NAME-terminal.desktop
echo "Type=Application"     >> launchers/$NAME-terminal.desktop
echo "Name=$NAME Terminal"  >> launchers/$NAME-terminal.desktop
echo "Terminal=true"        >> launchers/$NAME-terminal.desktop
echo "Exec=sh -c \"echo '$NAME'; $cmd\""            >> launchers/$NAME-terminal.desktop
chmod a+x                      launchers/$NAME-terminal.desktop
  
EOF
chmod a+x $WORK_DIR/build

if [ "x$BUILD" = "x1" ]; then
  # build the image
  (cd $WORK_DIR && ./build)

else
  echo "INFO: To build this image, use: cd $WORK_DIR; ./build"
  echo " "
  cat $WORK_DIR/build
fi

# ------------------------------------------------------------------------------
# get executables and Desktop launchers (from the Docker)
echo "------------------------------------------------------" >> $WORK_DIR/README
B=$(grep '\.desktop' $WORK_DIR/file_list.txt) || echo " "
echo "$B"                                                     >> $WORK_DIR/README

FILE=$WORK_DIR/start
dd status=none of=${FILE} << EOF
#!/bin/bash
# created by $0 on $DATE
#
# start a container from image $NAME
#
# Usage: start [CMD]
#   default CMD is $CMD

$cmd \$@
EOF
chmod a+x $WORK_DIR/start

# display final message
echo "--------------------------------------------"
echo "The image $NAME has been prepared in $WORK_DIR"
echo "  Desktop launchers are available in $WORK_DIR/launchers"
echo "To start $NAME, use:"
echo "  cd $WORK_DIR; ./start [cmd]"
echo " "