summaryrefslogtreecommitdiffhomepage
path: root/ci/linux-repository-builder/build-linux-repositories.sh
blob: a2fbf91f72992cefc67de5d786ed9e5011c9e57f (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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#!/usr/bin/env bash
#
# Builds linux deb and rpm repositories and uploads them to a repository server.
# One instance of this script targets *one* server environment.
# This means that if you want to publish to development, staging and production servers,
# you need to run three instances of this script.
#
# This script works on an $inbox_dir. In this directory it expects one directory per repository.
# For each repository it will read all files having the .src extension 
# These files are expected to contain a single line, a path to some directory where
# it can read new artifacts for that product.
# All deb and rpm files from that directory will be signed and moved over to a folder with
# the same name as the .src file, but with a .latest extension instead.
# So artifacts read from `app.src` will be moved to `app.latest/`.
#
# Then the deb and rpm repositories will be generated and all deb and rpm files in
# $inbox_dir/$repository/*.latest/ will be added to the repository. These repositories are then synced
# to `$repository_server_upload_domain respectively.

set -eu
# nullglob is needed to produce expected results when globing an empty directory
shopt -s nullglob

function usage() {
    echo "Usage: $0 <environment>"
    echo
    echo "Example usage: ./build-linux-repositories.sh production"
    echo
    echo "This script reads an inbox folder for the corresponding server environment."
    echo "It then generates and uploads new Linux repositories for all"
    echo "the latest artifacts"
    echo
    echo "Options:"
    echo "  -h | --help		Show this help message and exit."
    exit 1
}

if [[ "$#" == 0 || $1 == "-h" || $1 == "--help" ]]; then
    usage
fi

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

# shellcheck source=ci/linux-repository-builder/build-linux-repositories-config.sh
source "$SCRIPT_DIR/build-linux-repositories-config.sh"

environment="$1"
case "$environment" in
    "production")
        repository_server_upload_domain="$PRODUCTION_REPOSITORY_SERVER"
        repository_server_public_url="$PRODUCTION_LINUX_REPOSITORY_PUBLIC_URL"
        bunnycdn_pull_zone_id="$PRODUCTION_BUNNYCDN_PULL_ZONE_ID"
        ;;
    "staging")
        repository_server_upload_domain="$STAGING_REPOSITORY_SERVER"
        repository_server_public_url="$STAGING_LINUX_REPOSITORY_PUBLIC_URL"
        bunnycdn_pull_zone_id="$STAGING_BUNNYCDN_PULL_ZONE_ID"
        ;;
    "dev")
        repository_server_upload_domain="$DEV_REPOSITORY_SERVER"
        repository_server_public_url="$DEV_LINUX_REPOSITORY_PUBLIC_URL"
        ;;
    *)
        echo "Unknown environment. Specify production, staging or dev" >&2
        exit 1
        ;;
esac

inbox_dir="$LINUX_REPOSITORY_INBOX_DIR_BASE/$environment"

if [[ ! -d "$inbox_dir" ]]; then
    echo "Inbox $inbox_dir does not exist" 1>&2
    exit 1
fi

# Process all .src files in the given inbox dir.
# Signs and moves all found artifacts into .latest directories
# Returns 0 if everything went well and there are new artifacts for a product.
# Returns 1 if no new artifacts were found
function process_inbox {
    local inbox_dir=$1
    echo "[#] Processing inbox at $inbox_dir"

    local found_new_artifacts="false"
    # Read all notify files and move the artifacts they point to into a local .latest copy
    for notify_file in "$inbox_dir"/*.src; do
        if [[ ! -f "$notify_file" ]]; then
            echo "Ignoring non-file $notify_file" 1>&2
            continue
        fi
        echo "Processing notify file $notify_file"

        local src_dir
        src_dir=$(cat "$notify_file")
        if [[ ! -d "$src_dir" ]]; then
            echo "Artifact source dir $src_dir from notify file $notify_file does not exist" 1>&2
            continue
        fi

        # Removing the file before we move the artifacts out of where it points.
        # This ensures we don't get stuck in a loop processing it over and over
        # if the signing and moving fails.
        rm "$notify_file"

        local artifact_dir=${notify_file/%.src/.latest}
        # Recreate the artifact dir, cleaning it
        rm -rf "$artifact_dir" && mkdir -p "$artifact_dir" || exit 1

        # The fact that we have processed one .src file is enough tro trigger a repository
        # rebuild. Because if a product would like to publish "no artifacts" they should
        # be able to create a .src file pointing to an empty directory
        found_new_artifacts="true"

        echo "Moving artifacts from $src_dir to $artifact_dir"
        # Move all deb and rpm files into the .latest dir
        for src_deb in "$src_dir"/*.deb; do
            echo "Signing and moving $src_deb into $artifact_dir/"
            dpkg-sig --sign builder "$src_deb"
            mv "$src_deb" "$artifact_dir/"
        done
        for src_rpm in "$src_dir"/*.rpm; do
            echo "Signing and moving $src_rpm into $artifact_dir/"
            rpm --addsign "$src_rpm"
            mv "$src_rpm" "$artifact_dir/"
        done
        rm -r "$src_dir" || echo "Failed to remove src dir $src_dir"
    done

    if [[ $found_new_artifacts == "false" ]]; then
        return 1
    fi
    return 0
}

function rsync_repo {
    local local_repo_dir=$1
    local remote_repo_dir=$2

    echo "Syncing to $repository_server_upload_domain:$remote_repo_dir"
    # We have an issue where the rsync can fail due to the remote dir being locked (only one rsync at a time allowed)
    # We suspect this is because of too fast subsequent invocations of rsync to the same target dir. With a hacky sleep
    # we hope to avoid this issue for now.
    sleep 10
    rsync -av --delete --mkpath --rsh='ssh -p 1122' \
        "$local_repo_dir"/ \
        build@"$repository_server_upload_domain":"$remote_repo_dir"
}

function invalidate_bunny_cdn_cache {
    local pull_zone_id=$1
    curl --request POST \
        --url "https://api.bunny.net/pullzone/${pull_zone_id}/purgeCache" \
        --header "AccessKey: ${BUNNYCDN_API_KEY}" \
        --header 'content-type: application/json' \
        --fail-with-body
}

repositories_were_updated="false"
for repository in "${REPOSITORIES[@]}"; do
    deb_remote_repo_dir="deb/$repository"
    rpm_remote_repo_dir="rpm/$repository"
    # Stable or beta
    release_channel="$repository"

    repository_inbox_dir="$inbox_dir/$repository"
    if ! process_inbox "$repository_inbox_dir"; then
        echo "Nothing new in inbox at $repository_inbox_dir"
        continue
    fi

    # Read all .latest artifact dirs into array
    readarray -d '' artifact_dirs < <(find "$repository_inbox_dir" -maxdepth 1 -name "*.latest" -type d -print0)
    if [ "${#artifact_dirs[@]}" -lt 1 ]; then
        echo "No artifact directories in $repository_inbox_dir to generate repositories from" >&2
        continue
    fi

    echo "Generating repositories from these artifact directories: ${artifact_dirs[*]}"

    # Generate deb repository from all the .latest artifacts

    deb_repo_dir="$repository_inbox_dir/repos/deb"
    rm -rf "$deb_repo_dir" && mkdir -p "$deb_repo_dir" || exit 1
    "$SCRIPT_DIR/prepare-apt-repository.sh" "$release_channel" "$deb_repo_dir" "${artifact_dirs[@]}"

    # Generate rpm repository from all the .latest artifacts

    rpm_repo_dir="$repository_inbox_dir/repos/rpm"
    rm -rf "$rpm_repo_dir" && mkdir -p "$rpm_repo_dir" || exit 1
    "$SCRIPT_DIR/prepare-rpm-repository.sh" "$rpm_repo_dir" \
        "$repository_server_public_url" "$rpm_remote_repo_dir" "$repository" "${artifact_dirs[@]}"

    # rsync repositories to repository server

    echo "[#] Syncing deb repository to $deb_remote_repo_dir"
    rsync_repo "$deb_repo_dir" "$deb_remote_repo_dir"
    echo "[#] Syncing rpm repository to $rpm_remote_repo_dir"
    rsync_repo "$rpm_repo_dir" "$rpm_remote_repo_dir"

    echo "[#] ==== Done updating $repository in $environment ===="
    repositories_were_updated="true"
done

if [[ "$repositories_were_updated" == "true" ]]; then
    if [[ "$environment" == "production" || "$environment" == "staging" ]]; then
        echo "[#] Invalidating Bunny CDN cache for pull zone $bunnycdn_pull_zone_id"
        invalidate_bunny_cdn_cache "$bunnycdn_pull_zone_id"
    fi
fi