aboutsummaryrefslogtreecommitdiffstats
path: root/.extras/image-builder/build-full-image.sh
blob: 8c4476e4515bea1aa3fe22bcb501a9c8ff3f830a (plain)
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
#!/bin/bash
# build-full-image.sh — sbo-full:{ver} FROM sbo-base:{ver}, all series.
# Adapted from forge slackware/docker-images. No root needed.
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
source "${HERE}/config"
LOG_TAG=full
source "${HERE}/lib.sh"

BASE_IMAGE="${REGISTRY}/sbo-base"
FULL_IMAGE="${REGISTRY}/sbo-full"
DIGEST_LABEL="sbo.full.base-digest"

# ============================================================================
# Argument parsing
# ============================================================================

FORCE=false
OPT_VERSION=""
while [[ $# -gt 0 ]]; do
    case "$1" in
        --version) OPT_VERSION="$2"; shift 2 ;;
        --force)   FORCE=true;       shift   ;;
        *) _err "unknown argument: $1" ;;
    esac
done
if [[ -n "${OPT_VERSION}" ]]; then
    BUILD_VARIANTS=("${OPT_VERSION}")
else
    BUILD_VARIANTS=("${VARIANTS[@]}")
fi

# ============================================================================
# Tag derivation
# ============================================================================

# derive_tags VERSION
# Sets globals BASE_TAG and FULL_TAG for the given variant.
derive_tags() {
    local VERSION="$1"
    BASE_TAG="${BASE_IMAGE}:${VERSION}"
    FULL_TAG="${FULL_IMAGE}:${VERSION}"
}

# ============================================================================
# Rebuild detection
#
# After pulling the latest base image, its digest is compared with the
# digest stored in the DIGEST_LABEL on the current full image.  If
# they differ the base image has been updated and the full must be
# rebuilt to incorporate slackpkg package updates shipped with the new base.
# ============================================================================

needs_rebuild() {
    local BASE_TAG="$1" FULL_TAG="$2"

    if [[ "${FORCE}" == "true" ]]; then
        _log "  --force specified; rebuilding unconditionally."
        return 0
    fi

    # Digest of the base image we just pulled
    local BASE_DIGEST
    BASE_DIGEST=$(docker inspect --format '{{index .RepoDigests 0}}' \
                  "${BASE_TAG}" 2>/dev/null || echo "")
    if [[ -z "${BASE_DIGEST}" ]]; then
        _warn "  Could not determine base image digest; rebuilding to be safe."
        return 0
    fi

    # Digest recorded in the full image's label (empty if image doesn't exist)
    local RECORDED_DIGEST
    RECORDED_DIGEST=$(docker inspect \
                      --format "{{index .Config.Labels \"${DIGEST_LABEL}\"}}" \
                      "${FULL_TAG}" 2>/dev/null || echo "")

    if [[ "${BASE_DIGEST}" == "${RECORDED_DIGEST}" ]]; then
        _log "  Base image unchanged (${BASE_DIGEST:0:40}...); skipping."
        return 1
    fi

    _log "  Base image changed; rebuilding."
    _log "    Was : ${RECORDED_DIGEST:0:60}..."
    _log "    Now : ${BASE_DIGEST:0:60}..."
    return 0
}

# ============================================================================
# Build one variant
# ============================================================================

build_variant() {
    local VERSION="$1"
    derive_tags "${VERSION}"

    _log "=== ${FULL_TAG} ==="

    # Pull the latest base image so digest comparison is against the current
    # state of the registry, not a stale local cache.
    _log "  Pulling base image ${BASE_TAG}..."
    docker pull "${BASE_TAG}"

    if ! needs_rebuild "${BASE_TAG}" "${FULL_TAG}"; then
        return 0
    fi

    # Record the base image digest for the label
    local BASE_DIGEST
    BASE_DIGEST=$(docker inspect --format '{{index .RepoDigests 0}}' "${BASE_TAG}")

    local WORKDIR
    WORKDIR="$(mktemp -d /tmp/slackware-full.XXXXXX)"
    # shellcheck disable=SC2064
    trap "rm -rf '${WORKDIR}'" EXIT

    # Generate the Dockerfile for this variant
    cat > "${WORKDIR}/Dockerfile" <<DOCKERFILE
FROM ${BASE_TAG}
LABEL maintainer="Eric Hameleers <alien@slackware.com>"

# Update the slackpkg package list before installing so that we always
# get the versions current at build time, not the versions cached in the
# base image.
RUN LC_ALL=C slackpkg -batch=on -default_answer=y update

# Download and install each individual package (thanks aclemons):
RUN sed -i 's/DOWNLOAD_ALL=on/DOWNLOAD_ALL=off/' /etc/slackpkg/slackpkg.conf

# We don't care about a big firmware package:
RUN mv /etc/slackpkg/blacklist{,.keep} && \\
  echo kernel-firmware > /etc/slackpkg/blacklist

# Install the full Slackware:
# All packages are installed in a single RUN layer to keep the image lean.
RUN export LC_ALL=C && \\
  for series in a ap d e f k kde l n t tcl x xap xfce y ; do \\
  slackpkg -delall=on -batch=on -default_answer=y install "\$series"/* ; done

# And back to defaults:
RUN sed -i 's/DOWNLOAD_ALL=off/DOWNLOAD_ALL=on/' /etc/slackpkg/slackpkg.conf
RUN mv /etc/slackpkg/blacklist{.keep,}


# Delete package cache:
RUN rm -rf /var/cache/packages/*

# Rebuild the linker cache after adding new libraries
RUN /sbin/ldconfig

LABEL org.opencontainers.image.created="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
LABEL org.opencontainers.image.title="Slackware Linux ${VERSION} full (x86_64)"
LABEL org.opencontainers.image.description="Slackware Linux ${VERSION} full image with all packages installed (x86_64)"
LABEL org.opencontainers.image.vendor="Slackware Linux"
LABEL org.opencontainers.image.licenses="GPL-2.0-or-later AND LGPL-2.1-or-later"
LABEL org.opencontainers.image.url="http://www.slackware.com"
LABEL org.opencontainers.image.source="https://forge.slackware.nl/slackware/docker-images"
LABEL org.opencontainers.image.revision="${VERSION}"
LABEL slackware.version="${VERSION}"
LABEL slackware.arch="x86_64"
LABEL ${DIGEST_LABEL}="${BASE_DIGEST}"
CMD ["/bin/bash"]
DOCKERFILE

    local BUILD_FLAGS=()
    [[ "${FORCE}" == "true" ]] && BUILD_FLAGS+=(--no-cache)

    _log "  Building ${FULL_TAG}..."
    docker build "${BUILD_FLAGS[@]}" \
        -t "${FULL_TAG}" \
        "${WORKDIR}"

    _log "  Pushing ${FULL_TAG}..."
    docker push "${FULL_TAG}"

    _log "=== Done: ${FULL_TAG} ==="
}

# ============================================================================
# Main
# ============================================================================

rc=0
for VERSION in "${BUILD_VARIANTS[@]}"; do
    if ! ( build_variant "${VERSION}" ); then
        _warn "variant ${VERSION} failed; continuing."
        rc=1
    fi
done
exit "${rc}"