nandi/frqpublic Fork 0
5693bd54d43c2a63f33ffe43d67bc4909e271eb3
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

CI builds the image, Modal serves it 5693bd5 · on 5693bd54d43c2a63f33ffe43d67bc4909e271eb3 · nandi · 5h ago
.gitlab-ci.yml · 123 lines · 5.4 KBYAML Blame HistoryRaw
  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
# Two jobs, and neither needs a Flutter.
#
# Nothing is built here. The check reads source and no more, so this file needs
# no toolchain at all, which is what keeps it honest about running on every
# push. There used to be a second, scheduled job that re-resolved the
# jolt-native flake input; there is no jolt half any more and no input to
# follow, so there is nothing for a schedule to do.
stages: [check, image, deploy]

# Two toolchains and one artifact between them.
#
# The library is built once, in the image that has Nim, and the Dart job takes
# it rather than installing a second toolchain to rebuild it. That is not only
# tidier: `libfrqcore.so` links OpenSSL and is glibc, so it has to be built
# somewhere its runtime can load it — which is why the Nim job is the Debian
# image and not the Alpine one it used to be. A musl build would not load in
# `dart:*` at all.

# common/ is compiled by ClojureDart for two targets — the APK and the Linux
# desktop — and shared code reaching for the JVM or a `dart:` library breaks
# one of them at a namespace nobody touched. `Math/ceil` in the compose bar was
# the third time. Reading the source is enough to catch it, which is why this
# needs no toolchain and no builder: python and a checkout, a few seconds, on
# every push.
# The Nim core's tests. A second job rather than a step in the first, because
# it wants a compiler where check-common wants nothing: one can fail without
# hiding the other, and the pair of them is still seconds.
# The Nim core: its own suite, and the library the Dart job needs.
#
# `libssl-dev` because `nim/nim.cfg` sets `-d:ssl` — std/net links -lssl and
# -lcrypto for the TLS on :6697, and `frq.atproto` uses httpclient over the
# same. Without it the compile fails on a missing -lcrypto, which reads as
# nothing to do with TLS.
nim-test:
  stage: check
  image: nimlang/nim:2.2.10
  before_script:
    - apt-get update -qq && apt-get install -y -qq libssl-dev
  script:
    - cd nim && for t in tests/t*.nim; do nim c -r --hints:off --path:src "$t"; done
    - nim c --app:lib --mm:orc -d:release --hints:off --path:src
        --out:../build/nim/libfrqcore.so src/frq_core.nim
    # Diagnostic, not a gate. Worth reading rather than skimming: Nim resolves
    # OpenSSL through dlopen rather than a link-time NEEDED, so libssl does NOT
    # appear here and the Dart job still has to install one. A build that did
    # link it would show libssl.so.3 and libcrypto.so.3.
    - objdump -p ../build/nim/libfrqcore.so | grep NEEDED || true
  artifacts:
    paths: [build/nim/libfrqcore.so]
    expire_in: 1 hour

# The Dart side of the same boundary, on the plain VM — no Flutter, no
# emulator. It dlopens the library the job above built, so `libssl3` has to be
# there: the Dart SDK carries its own BoringSSL and does not bring OpenSSL
# with it.
dart-test:
  stage: check
  image: dart:3.13
  needs: [nim-test]
  before_script:
    - apt-get update -qq && apt-get install -y -qq libssl3
  script:
    - cd dart/frq_core && dart pub get && dart test -r expanded

# The web bundle, as an image, built by CI.
#
# Kaniko rather than docker: a GitLab runner has no docker daemon to lend and
# this needs no privileged mode. The context is the repo and the Dockerfile
# is `.modal/web/Dockerfile`, whose first stage is the same pinned Flutter and
# Nim that `just build web` uses -- so this is not a second way to build the
# web target, it is the first one inside an image.
#
# `--cache=true` makes the toolchain layer a pull rather than a fetch on every
# push: the apt line and the COPY above it change rarely, and the gigabyte
# behind them is the slow half.
build-web-image:
  stage: image
  needs: [nim-test]
  image:
    name: gcr.io/kaniko-project/executor:v1.23.2-debug
    entrypoint: [""]
  script:
    - mkdir -p /kaniko/.docker
    - |
      cat > /kaniko/.docker/config.json <<EOF
      {"auths":{"$CI_REGISTRY":{"username":"$CI_REGISTRY_USER","password":"$CI_REGISTRY_PASSWORD"}}}
      EOF
    - /kaniko/executor
        --context "$CI_PROJECT_DIR"
        --dockerfile "$CI_PROJECT_DIR/.modal/web/Dockerfile"
        --destination "$CI_REGISTRY_IMAGE/web:$CI_COMMIT_SHA"
        --destination "$CI_REGISTRY_IMAGE/web:latest"
        --cache=true
        --cache-repo "$CI_REGISTRY_IMAGE/web-cache"
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

# ...and Modal deploys that image, without rebuilding it.
#
# `modal deploy` and not `modal run`: a run is a job that ends, and this is a
# URL that should still be there on the next push. The app is named by
# `[container] name` in `.modal/web/container.toml`, so deploying again
# replaces the running one rather than starting a second.
#
# Two things have to exist outside this file. MODAL_TOKEN_ID and
# MODAL_TOKEN_SECRET are CI variables (masked, protected); and a Modal Secret
# named `gitlab-registry` holds REGISTRY_USERNAME / REGISTRY_PASSWORD for a
# GitLab deploy token with `read_registry`, because the image above is private
# and Modal pulls it on every cold start rather than once here.
deploy-web:
  stage: deploy
  needs: [build-web-image]
  image: python:3.13-slim
  variables:
    # The tag, not `latest`: `${FRQ_WEB_IMAGE}` in container.toml expands to
    # this, so what is deployed is the commit that was built and no other.
    FRQ_WEB_IMAGE: "$CI_REGISTRY_IMAGE/web:$CI_COMMIT_SHA"
  before_script:
    - pip install --quiet --no-cache-dir modal
  script:
    - modal deploy .modal/web/container.py
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH