]>
Commit | Line | Data |
---|---|---|
acc76a6e | 1 | #!/usr/bin/env bash |
95a57228 | 2 | |
7e40a110 | 3 | # We want to catch any unexpected failure, and exit immediately |
b7efb43e | 4 | set -E |
95a57228 | 5 | |
7e40a110 | 6 | # Download helper for git, to be called from the download wrapper script |
50c8b7e9 | 7 | # |
91e776b5 YM |
8 | # Options: |
9 | # -q Be quiet. | |
10 | # -r Clone and archive sub-modules. | |
11 | # -o FILE Generate archive in FILE. | |
12 | # -u URI Clone from repository at URI. | |
13 | # -c CSET Use changeset CSET. | |
14 | # -n NAME Use basename NAME. | |
50c8b7e9 YM |
15 | # |
16 | # Environment: | |
19afad50 | 17 | # GIT : the git command to call |
95a57228 | 18 | |
b7efb43e YM |
19 | # Save our path and options in case we need to call ourselves again |
20 | myname="${0}" | |
21 | declare -a OPTS=("${@}") | |
22 | ||
23 | # This function is called when an error occurs. Its job is to attempt a | |
24 | # clone from scratch (only once!) in case the git tree is borked, or in | |
25 | # case an unexpected and unsupported situation arises with submodules | |
26 | # or uncommitted stuff (e.g. if the user manually mucked around in the | |
27 | # git cache). | |
28 | _on_error() { | |
29 | local ret=${?} | |
30 | ||
31 | printf "Detected a corrupted git cache.\n" >&2 | |
32 | if ${BR_GIT_BACKEND_FIRST_FAULT:-false}; then | |
33 | printf "This is the second time in a row; bailing out\n" >&2 | |
34 | exit ${ret} | |
35 | fi | |
36 | export BR_GIT_BACKEND_FIRST_FAULT=true | |
37 | ||
38 | printf "Removing it and starting afresh.\n" >&2 | |
39 | ||
40 | popd >/dev/null | |
41 | rm -rf "${git_cache}" | |
42 | ||
43 | exec "${myname}" "${OPTS[@]}" || exit ${ret} | |
44 | } | |
45 | ||
3dea23cf | 46 | verbose= |
f109e7ee | 47 | recurse=0 |
91e776b5 | 48 | while getopts "${BR_BACKEND_DL_GETOPTS}" OPT; do |
50c8b7e9 | 49 | case "${OPT}" in |
4bf1174a | 50 | q) verbose=-q; exec >/dev/null;; |
f109e7ee | 51 | r) recurse=1;; |
91e776b5 YM |
52 | o) output="${OPTARG}";; |
53 | u) uri="${OPTARG}";; | |
54 | c) cset="${OPTARG}";; | |
6d938bcb | 55 | d) dl_dir="${OPTARG}";; |
91e776b5 YM |
56 | n) basename="${OPTARG}";; |
57 | :) printf "option '%s' expects a mandatory argument\n" "${OPTARG}"; exit 1;; | |
50c8b7e9 YM |
58 | \?) printf "unknown option '%s'\n" "${OPTARG}" >&2; exit 1;; |
59 | esac | |
60 | done | |
95a57228 | 61 | |
91e776b5 | 62 | shift $((OPTIND-1)) # Get rid of our options |
ff559846 | 63 | |
57731568 | 64 | # Create and cd into the directory that will contain the local git cache |
a07da16e | 65 | git_cache="${dl_dir}/git" |
57731568 YM |
66 | mkdir -p "${git_cache}" |
67 | pushd "${git_cache}" >/dev/null | |
a07da16e | 68 | |
b7efb43e YM |
69 | # Any error now should try to recover |
70 | trap _on_error ERR | |
71 | ||
3f2bdd07 YM |
72 | # Caller needs to single-quote its arguments to prevent them from |
73 | # being expanded a second time (in case there are spaces in them) | |
74 | _git() { | |
a07da16e | 75 | eval GIT_DIR="${git_cache}/.git" ${GIT} "${@}" |
3f2bdd07 YM |
76 | } |
77 | ||
f1eb192e YM |
78 | # Create a warning file, that the user should not use the git cache. |
79 | # It's ours. Our precious. | |
80 | cat <<-_EOF_ >"${dl_dir}/git.readme" | |
81 | IMPORTANT NOTE! | |
82 | ||
83 | The git tree located in this directory is for the exclusive use | |
84 | by Buildroot, which uses it as a local cache to reduce bandwidth | |
85 | usage. | |
86 | ||
87 | Buildroot *will* trash any changes in that tree whenever it needs | |
88 | to use it. Buildroot may even remove it in case it detects the | |
89 | repository may have been damaged or corrupted. | |
90 | ||
91 | Do *not* work in that directory; your changes will eventually get | |
92 | lost. Do *not* even use it as a remote, or as the source for new | |
93 | worktrees; your commits will eventually get lost. | |
94 | _EOF_ | |
95 | ||
d48a1b0d YM |
96 | # Initialise a repository in the git cache. If the repository already |
97 | # existed, this is a noop, unless the repository was broken, in which | |
98 | # case this magically restores it to working conditions. In the latter | |
99 | # case, we might be missing blobs, but that's not a problem: we'll | |
100 | # fetch what we need later anyway. | |
101 | # | |
102 | # We can still go through the wrapper, because 'init' does not use the | |
103 | # path pointed to by GIT_DIR, but really uses the directory passed as | |
104 | # argument. | |
57731568 | 105 | _git init . |
6d938bcb | 106 | |
6dfaa33d | 107 | # Ensure the repo has an origin (in case a previous run was killed). |
a07da16e | 108 | if ! _git remote |grep -q -E '^origin$'; then |
6dfaa33d YM |
109 | _git remote add origin "'${uri}'" |
110 | fi | |
111 | ||
6d938bcb MH |
112 | _git remote set-url origin "'${uri}'" |
113 | ||
114 | # Try to fetch with limited depth, since it is faster than a full clone - but | |
115 | # that only works if the version is a ref (tag or branch). Before trying to do | |
116 | # a shallow clone we check if ${cset} is in the list provided by git ls-remote. | |
117 | # If not we fallback to a full fetch. | |
ef92d32f | 118 | # |
6d938bcb MH |
119 | # Messages for the type of clone used are provided to ease debugging in |
120 | # case of problems | |
9d1ab432 | 121 | git_done=0 |
6d938bcb MH |
122 | if [ -n "$(_git ls-remote origin "'${cset}'" 2>&1)" ]; then |
123 | printf "Doing a shallow fetch\n" | |
124 | if _git fetch "${@}" --depth 1 origin "'${cset}'"; then | |
ebe6154f | 125 | git_done=1 |
7e40a110 | 126 | else |
6d938bcb | 127 | printf "Shallow fetch failed, falling back to fetching all refs\n" |
ebe6154f YM |
128 | fi |
129 | fi | |
130 | if [ ${git_done} -eq 0 ]; then | |
6d938bcb | 131 | printf "Fetching all references\n" |
e2b71e85 | 132 | _git fetch origin |
6d938bcb | 133 | _git fetch origin -t |
13c89c2f YM |
134 | fi |
135 | ||
13c89c2f YM |
136 | # Try to get the special refs exposed by some forges (pull-requests for |
137 | # github, changes for gerrit...). There is no easy way to know whether | |
138 | # the cset the user passed us is such a special ref or a tag or a sha1 | |
139 | # or whatever else. We'll eventually fail at checking out that cset, | |
140 | # below, if there is an issue anyway. Since most of the cset we're gonna | |
141 | # have to clone are not such special refs, consign the output to oblivion | |
142 | # so as not to alarm unsuspecting users, but still trace it as a warning. | |
143 | if ! _git fetch origin "'${cset}:${cset}'" >/dev/null 2>&1; then | |
144 | printf "Could not fetch special ref '%s'; assuming it is not special.\n" "${cset}" | |
95a57228 YM |
145 | fi |
146 | ||
b7efb43e YM |
147 | # Check that the changeset does exist. If it does not, re-cloning from |
148 | # scratch won't help, so we don't want to trash the repository for a | |
149 | # missing commit. We just exit without going through the ERR trap. | |
428a0649 YM |
150 | if ! _git rev-parse --quiet --verify "'${cset}^{commit}'" >/dev/null 2>&1; then |
151 | printf "Commit '%s' does not exist in this repository\n." "${cset}" | |
152 | exit 1 | |
153 | fi | |
154 | ||
f109e7ee YM |
155 | # Checkout the required changeset, so that we can update the required |
156 | # submodules. | |
3d2e0188 YM |
157 | _git checkout -f -q "'${cset}'" |
158 | ||
159 | # Get rid of now-untracked directories (in case a git operation was | |
160 | # interrupted in a previous run). | |
161 | _git clean -ffdx | |
3abd5ba4 YM |
162 | |
163 | # Get date of commit to generate a reproducible archive. | |
164 | # %cD is RFC2822, so it's fully qualified, with TZ and all. | |
1e39f087 | 165 | date="$( _git log -1 --pretty=format:%cD )" |
3abd5ba4 | 166 | |
f109e7ee YM |
167 | # There might be submodules, so fetch them. |
168 | if [ ${recurse} -eq 1 ]; then | |
169 | _git submodule update --init --recursive | |
170 | fi | |
171 | ||
6d938bcb MH |
172 | # Generate the archive, sort with the C locale so that it is reproducible. |
173 | # We do not want the .git dir; we keep other .git files, in case they are the | |
174 | # only files in their directory. | |
5d6ec40b RM |
175 | # The .git dir would generate non reproducible tarballs as it depends on |
176 | # the state of the remote server. It also would generate large tarballs | |
177 | # (gigabytes for some linux trees) when a full clone took place. | |
6d938bcb MH |
178 | find . -not -type d \ |
179 | -and -not -path "./.git/*" >"${output}.list" | |
180 | LC_ALL=C sort <"${output}.list" >"${output}.list.sorted" | |
95a57228 | 181 | |
0f369a92 AV |
182 | # Create GNU-format tarballs, since that's the format of the tarballs on |
183 | # sources.buildroot.org and used in the *.hash files | |
6e2f5d02 YM |
184 | tar cf - --transform="s#^\./#${basename}/#" \ |
185 | --numeric-owner --owner=0 --group=0 --mtime="${date}" --format=gnu \ | |
6d938bcb | 186 | -T "${output}.list.sorted" >"${output}.tar" |
04a22cf1 | 187 | gzip -6 -n <"${output}.tar" >"${output}" |
6d938bcb MH |
188 | |
189 | rm -f "${output}.list" | |
190 | rm -f "${output}.list.sorted" | |
191 | ||
192 | popd >/dev/null |