Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bring back our CI #47

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/actions/expected-failure/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---

inputs:
step-outcome:
description: 'Outcome of the step to consider'
expected-failure:
description: 'Whether a failure is expected'

runs:
using: composite

steps:
- if: ${{ inputs.step-outcome == 'success'
&& inputs.expected-failure != 'expected-failure' }}
shell: bash
run: |
echo "The step succeeded and was planned to succeed."
exit 0

- if: ${{ inputs.step-outcome != 'success'
&& inputs.expected-failure == 'expected-failure' }}
shell: bash
run: |
echo "The step failed and was planned to fail."
exit 0

- if: ${{ inputs.step-outcome == 'success'
&& inputs.expected-failure == 'expected-failure' }}
shell: bash
run: |
echo "The step succeeded but was planned to fail."
exit 1

- if: ${{ inputs.step-outcome != 'success'
&& inputs.expected-failure != 'expected-failure' }}
shell: bash
run: |
echo "The step failed but was planned to succeed."
exit 1
36 changes: 36 additions & 0 deletions .github/actions/run-tests/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---

inputs:
topiary:
description: 'What `topiary` command to test'
samples:
description: 'Path to the test samples'

runs:
using: composite

steps:
- name: Run a smoke test
shell: bash
run: |
## Run a tiny smoke test; checking that Topiary manages to start,
## parse a simple OCaml command and output the right thing.
echo 'open Foo' > expected.ml
echo 'open Foo' \
| ${{ inputs.topiary }} format --language ocaml \
> result.ml
diff expected.ml result.ml

- name: Run proper tests
shell: bash
run: |
## Test on OCaml implementation files (.ml)
cat ${{ inputs.samples }}/input/ocaml.ml \
| ${{ inputs.topiary }} format --language ocaml \
> ocaml.ml
diff ${{ inputs.samples }}/expected/ocaml.ml ocaml.ml
## Test on OCaml interface files (.mli)
cat ${{ inputs.samples }}/input/ocaml-interface.mli \
| ${{ inputs.topiary }} format --language ocaml_interface \
> ocaml-interface.mli
diff ${{ inputs.samples }}/expected/ocaml-interface.mli ocaml-interface.mli
17 changes: 17 additions & 0 deletions .github/workflows/ci.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## Get an OPAM image. The tag can be changed by the `--build-arg` argument to
## the `docker build` command line. It will use an Alpine distribution by
## default.
ARG tag=alpine
FROM ocaml/opam:$tag

## Copy the build context to a working directory, then pin the working directory
## and install topiary and its external dependencies.
WORKDIR /home/opam/topiary-opam
ADD . .
RUN sudo chown -R opam .
RUN opam pin add --no-action topiary.dev .
RUN opam depext --install topiary

## Create a symbolic link in a consistent place and use that as an entrypoint.
RUN opam exec -- sh -c 'ln -s $(command -v topiary) /home/opam/topiary'
ENTRYPOINT [ "/home/opam/topiary" ]
133 changes: 105 additions & 28 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,47 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

## Our CI consists in two kind of jobs: the `docker-based` ones and the
## `github-hosted` ones. Both come with their upsides and downsides.
##
## - `docker-based` jobs rely on the `ocaml/opam` Docker images [1]. These come
## with a variety of tags allowing to selected between various versions of
## OCaml and, more importantly for us, between various OSs (several Linux
## distributions as well as some Windows images). However, OPAM's sandboxing
## mechanism, based on `bubblewrap` [2], does not behave well in Docker and it
## is unavailable in those images [3].
##
## - `github-hosted` jobs rely on the default GitHub Actions runners and the
## `ocaml/setup-ocaml` action [4]. This offers more limited OSes (limited to
## Ubuntu, Windows and macOS) but it is compatible with OPAM's sandboxing
## mechanism.
##
## We need to test that our package behaves well with the OPAM sandboxing
## because Cargo does use network to build by default, which will not be
## available in the sandbox, and which we therefore need to catch in CI.
## Therefore, we need to include these `github-hosted` jobs.
##
## However, we also want to test that our package installs fine on a variety of
## distributions because it depends heavily on detecting external dependencies
## (namely Cargo and Rustc) and installing them correctly. Therefore, we also
## need to include these `docker-based` jobs.
##
## [1]: https://hub.docker.com/r/ocaml/opam
## [2]: https://github.com/containers/bubblewrap
## [3]: https://github.com/ocurrent/docker-base-images/issues/229
## [4]: https://github.com/ocaml/setup-ocaml

jobs:
install-and-test:
name: install-and-test

github-hosted:
name: github-hosted

strategy:
fail-fast: false
matrix:
os:
- ubuntu
- macos
include:
- {os: ubuntu}
- {os: macos}

runs-on: ${{ matrix.os }}-latest

Expand All @@ -48,6 +79,10 @@ jobs:

- name: Install Topiary
id: install-topiary
## `continue-on-error` will make this step succeed no matter what. The
## next step will then handle the outcome of this step manually. This is
## what allows us to emulate an expected CI failure.
continue-on-error: true
run: |
## Install Topiary then report where it is and in which version.
echo '::group::Install Topiary'
Expand All @@ -60,30 +95,72 @@ jobs:
opam exec -- sh -c 'topiary --version'
echo '::endgroup::'

- name: Run a smoke test
run: |
## Run a tiny smoke test; checking that Topiary manages to start,
## parse a simple OCaml command and output the right thing.
echo 'open Foo' > expected.ml
echo 'open Foo' \
| opam exec -- topiary format --language ocaml \
> result.ml
diff expected.ml result.ml

- name: Run proper tests
- name: Check the outcome of the build
uses: ./.github/actions/expected-failure
with:
step-outcome: ${{ steps.install-topiary.outcome }}
expected-failure: ${{ matrix.ef }}

- name: Run tests
if: steps.install-topiary.outcome == 'success'
uses: ./.github/actions/run-tests
with:
topiary: opam exec -- topiary
samples: topiary/topiary-cli/tests/samples

docker-based:
name: docker-based

strategy:
fail-fast: false
matrix:
include:
- {tag: alpine, ef: expected-failure} # https://github.com/tree-sitter/tree-sitter/issues/4184
- {tag: archlinux}
- {tag: centos, ef: expected-failure}
- {tag: debian, ef: expected-failure}
- {tag: debian-testing}
- {tag: debian-unstable}
- {tag: fedora}
- {tag: oraclelinux, ef: expected-failure} # Rust 1.79 < 1.83 as of 5 Feb 2025
- {tag: opensuse}
- {tag: opensuse-tumbleweed}
- {tag: ubuntu, ef: expected-failure} # Rust 1.80 < 1.83 as of 5 Feb 2025
- {tag: ubuntu-lts, ef: expected-failure} # Rust 1.80 < 1.83 as of 5 Feb 2025

runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: true

- name: Build Docker image (see next step for failure)
id: build-docker-image
## `continue-on-error` will make this step succeed no matter what. The
## next step will then handle the outcome of this step manually. This is
## what allows us to emulate an expected CI failure.
continue-on-error: true
run: |
## Test on OCaml implementation files (.ml)
cat topiary/topiary-cli/tests/samples/input/ocaml.ml \
| opam exec -- topiary format --language ocaml \
> ocaml.ml
diff topiary/topiary-cli/tests/samples/expected/ocaml.ml ocaml.ml
## Test on OCaml interface files (.mli)
cat topiary/topiary-cli/tests/samples/input/ocaml-interface.mli \
| opam exec -- topiary format --language ocaml_interface \
> ocaml-interface.mli
diff \
topiary/topiary-cli/tests/samples/expected/ocaml-interface.mli \
ocaml-interface.mli
docker build \
. \
--tag topiary \
--file .github/workflows/ci.dockerfile \
--build-arg tag=${{matrix.tag}}

- name: Check the outcome of the build
uses: ./.github/actions/expected-failure
with:
step-outcome: ${{ steps.build-docker-image.outcome }}
expected-failure: ${{ matrix.ef }}

- name: Run tests
if: steps.build-docker-image.outcome == 'success'
uses: ./.github/actions/run-tests
with:
topiary: docker run --interactive topiary
samples: topiary/topiary-cli/tests/samples

flake-checks:
name: flake-checks
Expand Down
46 changes: 23 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Availability
------------

The compilation of Topiary depends on the presence of Rust on the system in a
recent enough version. Currently, Rust 1.70.0 is required. [Our continuous
recent enough version. Currently, Rust 1.83.0 is required. [Our continuous
integration] keeps track of several distributions whose Rust version is listed
here for convenience:

Expand All @@ -54,35 +54,35 @@ here for convenience:
| Debian | [![][debian_unstable]][rust] [![][debian_testing]][rust] [![][debian_12]][rust] [![][debian_11]][rust]
| Fedora | [![][fedora_rawhide]][rust] [![][fedora_38]][rust] [![][fedora_37]][rust] [![][fedora_36]][rust]
| openSUSE | [![][opensuse_tumbleweed]][rust] [![][opensuse_leap_15_5]][rust] [![][opensuse_leap_15_4]][rust]
| Ubuntu | [![][ubuntu_23_10]][rust] [![][ubuntu_23_04]][rust] [![][ubuntu_22_04]][rust]
| Ubuntu | [![][ubuntu_24_10]][rust] [![][ubuntu_24_04]][rust] [![][ubuntu_23_04]][rust]

[alpine_edge]: https://repology.org/badge/version-for-repo/alpine_edge/rust.svg?header=Edge&minversion=1.70.0
[alpine_3_18]: https://repology.org/badge/version-for-repo/alpine_3_18/rust.svg?header=3.18&minversion=1.70.0
[alpine_3_17]: https://repology.org/badge/version-for-repo/alpine_3_17/rust.svg?header=3.17&minversion=1.70.0
[alpine_3_16]: https://repology.org/badge/version-for-repo/alpine_3_16/rust.svg?header=3.16&minversion=1.70.0
[alpine_edge]: https://repology.org/badge/version-for-repo/alpine_edge/rust.svg?header=Edge&minversion=1.83.0
[alpine_3_18]: https://repology.org/badge/version-for-repo/alpine_3_18/rust.svg?header=3.18&minversion=1.83.0
[alpine_3_17]: https://repology.org/badge/version-for-repo/alpine_3_17/rust.svg?header=3.17&minversion=1.83.0
[alpine_3_16]: https://repology.org/badge/version-for-repo/alpine_3_16/rust.svg?header=3.16&minversion=1.83.0

[arch]: https://repology.org/badge/version-for-repo/arch/rust.svg?header=&minversion=1.70.0
[arch]: https://repology.org/badge/version-for-repo/arch/rust.svg?header=&minversion=1.83.0

[centos_stream_8]: https://repology.org/badge/version-for-repo/centos_stream_8/rust.svg?header=Stream%208&minversion=1.70.0
[centos_stream_9]: https://repology.org/badge/version-for-repo/centos_stream_9/rust.svg?header=Stream%209&minversion=1.70.0
[centos_stream_8]: https://repology.org/badge/version-for-repo/centos_stream_8/rust.svg?header=Stream%208&minversion=1.83.0
[centos_stream_9]: https://repology.org/badge/version-for-repo/centos_stream_9/rust.svg?header=Stream%209&minversion=1.83.0

[debian_11]: https://repology.org/badge/version-for-repo/debian_11/rust.svg?header=11&minversion=1.70.0
[debian_12]: https://repology.org/badge/version-for-repo/debian_12/rust.svg?header=12&minversion=1.70.0
[debian_testing]: https://repology.org/badge/version-for-repo/debian_13/rust.svg?header=Testing&minversion=1.70.0
[debian_unstable]: https://repology.org/badge/version-for-repo/debian_unstable/rust.svg?header=Unstable&minversion=1.70.0
[debian_11]: https://repology.org/badge/version-for-repo/debian_11/rust.svg?header=11&minversion=1.83.0
[debian_12]: https://repology.org/badge/version-for-repo/debian_12/rust.svg?header=12&minversion=1.83.0
[debian_testing]: https://repology.org/badge/version-for-repo/debian_13/rust.svg?header=Testing&minversion=1.83.0
[debian_unstable]: https://repology.org/badge/version-for-repo/debian_unstable/rust.svg?header=Unstable&minversion=1.83.0

[fedora_rawhide]: https://repology.org/badge/version-for-repo/fedora_rawhide/rust.svg?header=Rawhide&minversion=1.70.0
[fedora_38]: https://repology.org/badge/version-for-repo/fedora_38/rust.svg?header=38&minversion=1.70.0
[fedora_37]: https://repology.org/badge/version-for-repo/fedora_37/rust.svg?header=37&minversion=1.70.0
[fedora_36]: https://repology.org/badge/version-for-repo/fedora_36/rust.svg?header=36&minversion=1.70.0
[fedora_rawhide]: https://repology.org/badge/version-for-repo/fedora_rawhide/rust.svg?header=Rawhide&minversion=1.83.0
[fedora_38]: https://repology.org/badge/version-for-repo/fedora_38/rust.svg?header=38&minversion=1.83.0
[fedora_37]: https://repology.org/badge/version-for-repo/fedora_37/rust.svg?header=37&minversion=1.83.0
[fedora_36]: https://repology.org/badge/version-for-repo/fedora_36/rust.svg?header=36&minversion=1.83.0

[opensuse_tumbleweed]: https://repology.org/badge/version-for-repo/opensuse_tumbleweed/rust.svg?header=Tumbleweed&minversion=1.70.0
[opensuse_leap_15_5]: https://repology.org/badge/version-for-repo/opensuse_leap_15_5/rust.svg?header=Leap%2015.5&minversion=1.70.0
[opensuse_leap_15_4]: https://repology.org/badge/version-for-repo/opensuse_leap_15_4/rust.svg?header=Leap%2015.4&minversion=1.70.0
[opensuse_tumbleweed]: https://repology.org/badge/version-for-repo/opensuse_tumbleweed/rust.svg?header=Tumbleweed&minversion=1.83.0
[opensuse_leap_15_5]: https://repology.org/badge/version-for-repo/opensuse_leap_15_5/rust.svg?header=Leap%2015.5&minversion=1.83.0
[opensuse_leap_15_4]: https://repology.org/badge/version-for-repo/opensuse_leap_15_4/rust.svg?header=Leap%2015.4&minversion=1.83.0

[ubuntu_23_10]: https://repology.org/badge/version-for-repo/ubuntu_23_10/rust.svg?header=23.10&minversion=1.70.0
[ubuntu_23_04]: https://repology.org/badge/version-for-repo/ubuntu_23_04/rust.svg?header=23.04&minversion=1.70.0
[ubuntu_22_04]: https://repology.org/badge/version-for-repo/ubuntu_22_04/rust.svg?header=22.04&minversion=1.70.0
[ubuntu_24_10]: https://repology.org/badge/version-for-repo/ubuntu_24_10/rust.svg?header=23.10&minversion=1.83.0
[ubuntu_24_04]: https://repology.org/badge/version-for-repo/ubuntu_24_04/rust.svg?header=23.04&minversion=1.83.0
[ubuntu_23_04]: https://repology.org/badge/version-for-repo/ubuntu_23_04/rust.svg?header=22.04&minversion=1.83.0

[rust]: https://repology.org/project/rust/versions

Expand Down
Loading