# NOTE: this workflow has never run. Authored 2026-08-25 and not # executable locally — the first real tag push is its first test. # Expect to adjust the toolchain step if the pinned OCaml/dune version # is not available on the runner image. name: release # Fires only on a version tag, so nothing is published by an ordinary # push. `workflow_dispatch` is the manual escape hatch for a re-run. on: push: tags: - 'v*' workflow_dispatch: # The ONE line that replaces `gh auth login`: it widens the automatic # GITHUB_TOKEN so this job may write releases. No PAT, no secret to # rotate, and the token dies with the job. permissions: contents: write jobs: release: # DELIBERATE, not a default. The release binaries link glibc # dynamically, so the build host's glibc caps which symbol versions # they can import — and that cap becomes the minimum glibc every # user needs. Built on 24.04 (glibc 2.39) the floor is 2.39; # built here on 22.04 (2.35) it is 2.35, which is the difference # between excluding and including Ubuntu 22.04, Debian 12 and # RHEL 9. Raise this image only with a reason, and update the # supported-systems list in docs/examples/site/install/view.wo in # the same change. runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 # The compiler is OCaml stdlib only — no opam packages — so this # step exists purely to get a compiler and dune onto the runner. - uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: '4.14' # The tag is the release's identity; VERSION is what the binaries # report. If they disagree the download URL would name a version # nobody can install. mkdist.sh already guards VERSION against the # binaries; this guards the tag against VERSION. - name: Tag must match VERSION run: | tag="${GITHUB_REF_NAME#v}" ver="$(cat VERSION)" [ "$tag" = "$ver" ] || { echo "tag $GITHUB_REF_NAME does not match VERSION $ver" >&2 exit 1 } - name: Build the tarball run: ./scripts/mkdist.sh # The site links one exact filename. If mkdist ever changes its # naming, the download button 404s for every visitor — so fail # here instead. - name: Asset name must match what the site links run: | ver="$(cat VERSION)" asset="writeonce-${ver}-linux-amd64.tar.gz" test -f "dist/$asset" grep -q "$asset" docs/examples/site/install/view.wo || { echo "$asset is not the filename /install links" >&2 exit 1 } - name: Verify the digest run: cd dist && sha256sum -c "writeonce-$(cat ../VERSION)-linux-amd64.tar.gz.sha256" # Prove the ARTEFACT works, using the binaries inside it rather # than the ones just built in the tree. This is what catches a # tarball that packaged the wrong thing. - name: Smoke-test the extracted toolchain run: | ver="$(cat VERSION)" tmp="$(mktemp -d)" tar -C "$tmp" -xzf "dist/writeonce-${ver}-linux-amd64.tar.gz" export PATH="$tmp/writeonce/bin:$PATH" woc version wovm --version mkdir -p "$tmp/hello" cd "$tmp/hello" printf 'name = "hello"\nversion = "0.1.0"\n\n[runtime]\nwo = ">= 0.1"\n' > wo.toml printf 'fn main() -> Int {\n print("hello, writeonce");\n return 0;\n}\n' > main.wo woc . out="$(./target/hello)" [ "$out" = "hello, writeonce" ] || { echo "got: $out" >&2; exit 1; } # Record the real glibc floor of what is about to ship, so the # claim on /install can be checked against a build log rather # than trusted. - name: Report the glibc floor run: | ver="$(cat VERSION)" tmp="$(mktemp -d)" tar -C "$tmp" -xzf "dist/writeonce-${ver}-linux-amd64.tar.gz" for b in "$tmp"/writeonce/bin/*; do printf '%s needs %s\n' "$(basename "$b")" \ "$(objdump -T "$b" | grep -oE 'GLIBC_[0-9.]+' | sort -uV | tail -1)" done # gh is preinstalled on GitHub runners and reads GH_TOKEN from the # environment, so there is no `gh auth login` anywhere in this file. - name: Publish env: GH_TOKEN: ${{ github.token }} run: | ver="$(cat VERSION)" gh release create "$GITHUB_REF_NAME" \ "dist/writeonce-${ver}-linux-amd64.tar.gz" \ "dist/writeonce-${ver}-linux-amd64.tar.gz.sha256" \ --title "writeonce ${ver}" \ --generate-notes