~bigbes/sr-ht-dolt

ref: ede3b0bb2671ffde35a66527b920c40eaccc096f sr-ht-dolt/.build.yml -rw-r--r-- 15.7 KiB
ede3b0bb — Eugene Blikh go.mod: take the shared libraries' current heads 2 days ago
                                                                                
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
# builds.sr.ht manifest for dolt.sr.ht. One linear pipeline: install the cache
# helper, assemble the shared SCSS, restore caches, package with abuild, publish
# the apk, save caches, and upload this build's own coverage and benchmarks to
# cov.sr.ht and bench.sr.ht.
#
# The reasoning behind every task lives in docs/ci.md, not here: builds.sr.ht
# stores the submitted manifest in a varchar(16384), so a manifest over 16 KiB
# cannot be submitted at all — and the failure is a branch with no CI, not a red
# build. Add paragraphs to docs/ci.md and a pointer here.
image: alpine/edge
packages:
  - abuild
  - curl
  - go
  - git
  - rclone
  - sassc
  - minify
  # For the database suites, not for the package — see docs/ci.md#packages.
  - postgresql
  - postgresql-client
secrets:
  # File secret `apk-ci-s3`, installed at ~/.apk-ci.env, containing
  # APK_CI_S3_ACCESS_KEY / APK_CI_S3_SECRET_KEY for the Garage `repo` bucket.
  - apk-ci-s3
  # S3 credentials for the cacher CI cache (Garage `docker-cache` bucket),
  # same pair the bencher and ci-cacher builds use.
  - 7dde4219-0783-4581-a67d-c94749de3600   # ~/.s3-cache-key-id
  - 0e5b3530-6f19-4f30-9b73-9339dd382e46   # ~/.s3-cache-key-secret
  # One tokens.sr.ht working token, the same one the siblings mount, carrying
  # artifacts:upload, cov:upload and bench:upload. See docs/ci.md#secrets.
  - c7968415-1a6d-4ca0-a188-150fb7f57b65   # ~/.srht-token
sources:
  - https://git.srht.bigb.es/~bigbes/sr-ht-dolt
environment:
  REPO: sr-ht-dolt
  APK_REPO: alpine/v3.22/bigbes/x86_64
  S3_BUCKET: repo
  S3_ENDPOINT: https://s3.bigb.es
  # The second destination of the same apk: one channel of artifacts.sr.ht
  # holds the packages of every sibling service, so a consumer adds one
  # repository line. See docs/ci.md#publish_artifacts.
  ARTIFACTS_ENDPOINT: https://artifacts.srht.bigb.es
  ARTIFACTS_CHANNEL: "~bigbes/main"
  ARTIFACTS_DIST: v3.22
  # Must track the srht deployment's SRHT_CORE_VER, or this service's theme
  # drifts from the rest of the instance. BOOTSTRAP_REV is the submodule commit
  # core.sr.ht pins at that tag; bump the two together. docs/ci.md#environment.
  CORE_VER: "0.84.5"
  BOOTSTRAP_REV: 779ad9f174ea5ab7e755f6df0ec9e5912d67dd16
  # Dogfooding. Both repo names are the `sources:` line read as ~owner/repo.
  # See docs/ci.md#coverage and docs/ci.md#bench.
  COVER_ORIGIN: https://cov.srht.bigb.es
  COVER_REPO: "~bigbes/sr-ht-dolt"
  BENCH_ORIGIN: https://bench.srht.bigb.es
  BENCH_REPO: "~bigbes/sr-ht-dolt"
# Literal paths relative to $HOME, and not a fallback. docs/ci.md#artifacts.
artifacts:
  - cover.out
  - bench.txt
submitter:
  git.sr.ht:
    allow-refs:
      - refs/heads/master
      # Tags build too, now that the version task reads them: pushing v0.2.0 is
      # what produces the 0.2.0 apk. See docs/ci.md#version.
      - "refs/tags/v*"
tasks:
  # S3-backed CI cache helper; installed first so scss can already use it.
  # install.sh's PATH export goes to ~/.buildenv, which only the NEXT task
  # sources — hence two tasks and not one. See docs/ci.md#cacher.
  - cacher_install: |
      curl -fsSL https://bigbes.pages.srht.bigb.es/ci-cacher/install.sh | sh
  - cacher_init: |
      cacher init \
        --endpoint    https://s3.bigb.es \
        --region      garage \
        --bucket      docker-cache \
        --prefix      sr-ht-dolt/deps \
        --key-file    ~/.s3-cache-key-id \
        --secret-file ~/.s3-cache-key-secret
  - scss: |
      # Assemble the shared sourcehut partials no apk ships, the way
      # core.sr.ht's `make install-scss` would, cached by the two pins so an
      # outage at git.sr.ht or github.com can't fail us. --exec runs on a miss
      # and seeds the cache after; it sees exported vars only, hence the inline
      # key and the single quotes. See docs/ci.md#scss.
      cacher dir download "scss/${CORE_VER}-${BOOTSTRAP_REV}.tar.zst" ~/scss --exec '
        git clone --depth 1 --branch "$CORE_VER" \
          https://git.sr.ht/~sircmpwn/core.sr.ht /tmp/core
        mkdir -p ~/scss/bootstrap
        cp /tmp/core/scss/*.scss /tmp/core/scss/*.css ~/scss/
        git init -q /tmp/bootstrap
        git -C /tmp/bootstrap remote add origin https://github.com/twbs/bootstrap
        git -C /tmp/bootstrap fetch -q --depth 1 origin "$BOOTSTRAP_REV"
        git -C /tmp/bootstrap checkout -q FETCH_HEAD
        cp -r /tmp/bootstrap/scss ~/scss/bootstrap/scss
      '
      sudo mkdir -p /usr/share/sourcehut
      sudo cp -r ~/scss /usr/share/sourcehut/scss
  - keygen: |
      # Throwaway signing key, and -i is not optional: docs/ci.md#keygen.
      SUDO=sudo abuild-keygen -a -n -i -q
  - version: |
      # ONE `git describe` decides the apk pkgver: a tag, else tag_git<n>, else
      # the family's commit count. The raw describe output is not a legal pkgver
      # and _git sorts AFTER the release: docs/ci.md#version.
      #
      # EXPORTED rather than sed-ed into the APKBUILD (which reads $PKGVER),
      # because rewriting a tracked file would flip the VCS stamp Go records in
      # every binary to dirty — do not "tidy" it back into a sed. The tree is
      # printed because this is the last moment it is provably clean.
      cd "$REPO"
      desc=$(git describe --tags --always --dirty)
      base=${desc%-dirty}
      case "$base" in
        v*-g*) n=${base%-g*}; ver="${n%-*}"; ver="${ver#v}_git${n##*-}" ;;
        v*)    ver="${base#v}" ;;
        *)     ver="0.0.$(git rev-list --count HEAD)" ;;
      esac
      echo "export PKGVER=$ver" >> ~/.buildenv
      echo "building $ver from $desc"
      git status --porcelain
  - cache_restore: |
      # Go module and build caches keyed by go.sum: the dolt dependency tree
      # dominates compile time and only changes when go.sum does. --optional
      # makes a miss a cold build, not an error. abuild re-pins both, since it
      # redirects the Go caches into its own $tmpdir (docs/ci.md#cache_restore).
      KEY_MOD=$(cacher key "gomod/{hash}.tar.zst" --hash-from "$REPO/go.sum")
      KEY_GOC=$(cacher key "gocache/{hash}.tar.zst" --hash-from "$REPO/go.sum")
      echo "export KEY_MOD=$KEY_MOD KEY_GOC=$KEY_GOC" >> ~/.buildenv
      cacher dir download "$KEY_MOD" ~/go/pkg/mod        --optional
      cacher dir download "$KEY_GOC" ~/.cache/go-build   --optional
      # Repair block for the HALF-restored module cache — the normal failure
      # here, not a freak one, and it reads like a code bug. Do not remove and
      # do not soften to `|| true`:
      # docs/ci.md#the-half-restored-module-cache.
      cd "$REPO"
      chmod -R u+w ~/go/pkg/mod 2>/dev/null || true
      if ! go mod verify >/dev/null 2>&1; then
        echo "restored module cache did not verify — discarding it"
        rm -rf ~/go/pkg/mod
      fi
      # `go mod download`, NOT `go mod download all`: `all` resolves the whole
      # module graph including dependencies' test deps and APPENDS their hashes
      # to go.sum, which is a modified tracked file and therefore a "-dirty"
      # apk. Measured. docs/ci.md#go-mod-download-and-the-word-all.
      go mod download
      go mod verify
      # Neither line above may rewrite go.mod or go.sum — a dirty tree here is a
      # "-dirty" apk, so check rather than trust. docs/ci.md#cache_restore.
      git status --porcelain
  - postgres: |
      # A real Postgres in the VM. Without it db/ skips 9 of its 10 tests and
      # the build goes green over the whole persistence layer — which is where
      # this repository had stood since it was written. Every flag below is
      # load-bearing: docs/ci.md#postgres.
      sudo install -d -o postgres -g postgres /run/postgresql /var/lib/postgresql/data
      sudo -u postgres initdb -D /var/lib/postgresql/data
      sudo -u postgres pg_ctl -D /var/lib/postgresql/data -l /tmp/pg.log -w start \
        -o "-k /run/postgresql -h 127.0.0.1 \
            -c fsync=off -c full_page_writes=off -c synchronous_commit=off"
      sudo -u postgres createuser -s "$(id -un)"
      sudo -u postgres createdb -O "$(id -un)" doltsrht_test
      echo "export DOLTSRHT_TEST_PG='postgresql://$(id -un)@127.0.0.1/doltsrht_test?sslmode=disable'" \
        >> ~/.buildenv
  - test: |
      cd "$REPO"
      # An empty DSN would skip every database suite and leave the build green
      # over untested code — and options="!check" in the APKBUILD says this task
      # is where the suites run, so a silent skip here is a package built on a
      # promise nothing kept. It also catches a reordering of the two tasks.
      # docs/ci.md#test.
      if [ -z "$DOLTSRHT_TEST_PG" ]; then
        echo "DOLTSRHT_TEST_PG is unset: the postgres task did not export it," >&2
        echo "so every database suite would skip and this build would lie." >&2
        exit 1
      fi
      # `make vet` and `make test` rather than bare go commands: the Makefile is
      # where -tags gms_pure_go and CGO_ENABLED=0 are named, and a second copy
      # of those here is a second copy to forget. docs/ci.md#test.
      make vet
      # `make cover` and not `make test`: same suites, plus the flags the upload
      # needs, so the profile is a by-product of the gate. docs/ci.md#test.
      make cover COVERPROFILE="$HOME/cover.out"
      # Printed, not gated, for the same reason as cache_restore's: this task
      # runs before abuild, so anything the suites left in the checkout is a
      # "-dirty" apk, and this is where it would show. docs/ci.md#test.
      git status --porcelain
  - build: |
      cd "$REPO"
      # -d: makedepends are already installed via `packages:` above.
      REPODEST=$HOME/packages abuild -d
      find "$HOME/packages" -name '*.apk'
  - publish: |
      # The gate is the honest answer to a build that was handed no secrets, not
      # a fallback: with ~/.apk-ci.env absent every earlier task has still run
      # and a signed apk is sitting in $HOME/packages. See docs/ci.md#publish.
      if [ ! -r ~/.apk-ci.env ]; then
        echo "no ~/.apk-ci.env: this build has no apk repo credentials"
        echo "the package was built and signed, and is not published"
        exit 0
      fi
      set +x   # never echo the S3 credentials into the build log
      . ~/.apk-ci.env
      export RCLONE_CONFIG_GARAGE_TYPE=s3
      export RCLONE_CONFIG_GARAGE_PROVIDER=Other
      export RCLONE_CONFIG_GARAGE_ENDPOINT="$S3_ENDPOINT"
      export RCLONE_CONFIG_GARAGE_REGION=garage
      export RCLONE_CONFIG_GARAGE_FORCE_PATH_STYLE=true
      export RCLONE_CONFIG_GARAGE_ACCESS_KEY_ID="$APK_CI_S3_ACCESS_KEY"
      export RCLONE_CONFIG_GARAGE_SECRET_ACCESS_KEY="$APK_CI_S3_SECRET_KEY"
      set -x
      # Upload only, never delete. See docs/ci.md#publish.
      find "$HOME/packages" -name '*.apk' -print | while read -r f; do
        rclone copyto "$f" "garage:$S3_BUCKET/$APK_REPO/$(basename "$f")"
        echo "uploaded $(basename "$f")"
      done
      echo "published; apk-mirror on phoebe re-indexes within 15 minutes"
  - publish_artifacts: |
      # The same apk into the artifacts.sr.ht channel, beside the S3 copy above.
      # The two destinations are independent on purpose: S3 is the road phoebe
      # still walks, this one is the road being opened. Its failure is its own,
      # and it never rolls back the upload that already succeeded.
      # See docs/ci.md#publish_artifacts.
      if [ ! -r ~/.srht-token ]; then
        echo "no ~/.srht-token: nothing was published to artifacts.sr.ht"
        exit 0
      fi
      count=$(find "$HOME/packages" -name '*.apk' | wc -l)
      test "$count" -gt 0 || { echo "no .apk under $HOME/packages" >&2; exit 1; }
      url="$ARTIFACTS_ENDPOINT/api/v1/pkg/$ARTIFACTS_CHANNEL/apk/$ARTIFACTS_DIST"
      # A `find | while read` would run the body in a subshell under ash and
      # lose $failed with it, so the loop reads a word list instead.
      failed=
      for file in $(find "$HOME/packages" -name '*.apk'); do
        set +x   # the token must not reach the log
        code=$(curl -sS -o /tmp/artifacts.out -w '%{http_code}' -X PUT \
          -H "Authorization: Bearer $(cat ~/.srht-token)" \
          --data-binary "@$file" "$url")
        set -x
        case "$code" in
        200|201)
          echo "published $(basename "$file") -> $code"
          ;;
        409)
          # Same version, other bytes. abuild stamps mtimes into the archive,
          # so resubmitting one commit builds a byte-different apk under the
          # same pkgver; the published copy stands and the build stays green.
          echo "WARNING: $(basename "$file") already published with other bytes; kept the published copy"
          ;;
        *)
          echo "FAILED $(basename "$file") -> $code" >&2
          cat /tmp/artifacts.out >&2
          echo >&2
          failed=1
          ;;
        esac
      done
      test -z "$failed" || exit 1
      echo "index: $ARTIFACTS_ENDPOINT/$ARTIFACTS_CHANNEL/apk/$ARTIFACTS_DIST/x86_64/APKINDEX.tar.gz"
  - cache_save: |
      # AFTER publish so an S3 hiccup cannot strand a good apk, and fatal on
      # purpose. Without --force an upload skips a key already there, so no
      # `cacher exists ||` guard is needed. See docs/ci.md#cache_save.
      cacher dir upload "$KEY_MOD" ~/go/pkg/mod
      cacher dir upload "$KEY_GOC" ~/.cache/go-build
  - coverage: |
      # Dogfooding: the profile the test task wrote, POSTed to this instance's
      # own cov.sr.ht. Before bench, whose run is minutes. docs/ci.md#coverage.
      cd "$REPO"
      # Missing or empty is a 400 about a body rather than about the build.
      test -s "$HOME/cover.out" || { echo "no ~/cover.out" >&2; exit 1; }
      if [ ! -r ~/.srht-token ]; then
        echo "no ~/.srht-token: no cov.sr.ht credentials in this build"
        echo "the profile is this build's cover.out artifact and is not lost"
        exit 0
      fi
      # Both ref prefixes stripped (this builds tags too), key is the idempotency
      # key, no Content-Type (the service sniffs), set +x so the header stays out
      # of the log, --fail-with-body so a rejection is loud and readable.
      # docs/ci.md#the-two-requests.
      ref="${GIT_REF#refs/heads/}"; ref="${ref#refs/tags/}"
      url="$COVER_ORIGIN/api/v1/repos/$COVER_REPO/reports"
      url="$url?commit=$(git rev-parse HEAD)&ref=$ref&key=$JOB_ID&job_url=$JOB_URL"
      echo "uploading cover.out to $url"
      set +x
      curl -sS --fail-with-body -X POST \
        -H "Authorization: Bearer $(cat ~/.srht-token)" \
        --data-binary "@$HOME/cover.out" \
        "$url"
      echo
  - bench: |
      # Dogfooding: this service's own benchmarks, to this instance's own
      # bench.sr.ht. Last and its own task on purpose, and this VM measures a
      # shape rather than a number. docs/ci.md#bench.
      cd "$REPO"
      # -s so the recipe is not echoed into the body, and a redirect and a cat
      # and NOT `| tee` — tee's exit status would let a failed run pass.
      make -s bench > "$HOME/bench.txt"
      cat "$HOME/bench.txt"
      # `go test -bench` matching nothing prints `ok` and exits 0, and an empty
      # body is valid benchfmt, so the names are checked. docs/ci.md#the-two-greps
      grep -q '^BenchmarkBoardBuild' "$HOME/bench.txt"
      grep -q '^BenchmarkMemoryBodyRender' "$HOME/bench.txt"
      if [ ! -r ~/.srht-token ]; then
        echo "no ~/.srht-token: no bench.sr.ht credentials in this build"
        echo "the run is above and is this build's bench.txt artifact"
        exit 0
      fi
      # The coverage request's shape, plus visibility= — which acts only on the
      # POST that creates $BENCH_REPO.
      ref="${GIT_REF#refs/heads/}"; ref="${ref#refs/tags/}"
      url="$BENCH_ORIGIN/api/v1/repos/$BENCH_REPO/runs"
      url="$url?commit=$(git rev-parse HEAD)&ref=$ref&key=$JOB_ID&job_url=$JOB_URL"
      url="$url&visibility=public"
      echo "uploading bench.txt to $url"
      set +x
      curl -sS --fail-with-body -X POST \
        -H "Authorization: Bearer $(cat ~/.srht-token)" \
        --data-binary "@$HOME/bench.txt" \
        "$url"
      echo