Skip to content

Commit 8ac2eb0

Browse files
authored
Move from Travis CI to GitHub Actions (#930)
Closes #921 The Exercism organisation is moving to GitHub Actions. It runs faster.
1 parent 3b9167c commit 8ac2eb0

7 files changed

+175
-82
lines changed

.github/workflows/tests.yml

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
name: Tests
2+
on:
3+
pull_request:
4+
push:
5+
branches:
6+
- master
7+
schedule:
8+
# Weekly.
9+
- cron: "0 0 * * 0"
10+
11+
jobs:
12+
track-config:
13+
name: Check track configuration
14+
runs-on: ubuntu-latest
15+
steps:
16+
# master is needed in addition to HEAD,
17+
# because the README check only checks exercises changed since master.
18+
- uses: actions/checkout@v2
19+
with:
20+
ref: master
21+
22+
- uses: actions/checkout@v2
23+
24+
- name: Fetch
25+
run: bin/fetch-configlet
26+
27+
- name: Lint
28+
run: bin/configlet lint .
29+
30+
- name: Ensure READMEs are updated
31+
run: sh ./bin/ensure-readmes-are-updated.sh
32+
33+
- name: Check configlet fmt
34+
run: sh ./bin/check-configlet-fmt.sh
35+
36+
- name: Install yq (for stack resolvers)
37+
run: |
38+
sudo add-apt-repository -y ppa:rmescandon/yq
39+
sudo apt-get -q update
40+
sudo apt-get -y install yq
41+
42+
- name: Ensure stack resolvers synced
43+
run: sh ./bin/ensure-stack-resolvers-synced.sh
44+
45+
- name: Check for invalid UUIDs
46+
# can be removed once `configlet lint` gains this ability.
47+
# Check issue https://github.com/exercism/configlet/issues/99
48+
run: |
49+
uuids=$(jq --raw-output '.exercises | map(.uuid) | .[]' config.json)
50+
bad_uuid=$(echo "$uuids" | grep -vE '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$' || true)
51+
if [ -n "$bad_uuid" ]; then
52+
echo "invalid UUIDs found! please correct these to be valid UUIDs:"
53+
echo "$bad_uuid"
54+
exit 1
55+
fi
56+
57+
exercises:
58+
name: Check exercises
59+
runs-on: ubuntu-latest
60+
strategy:
61+
matrix:
62+
resolver:
63+
# We say lts-from-exercises here instead of a specific version. Reason:
64+
# Required CI checks are designated by their name + matrix values.
65+
#
66+
# If we wrote lts-A.B here,
67+
# then we'd ask an admin to make "Check exercises (lts-A.B)" required.
68+
# But what happens when we update to lts-C.D?
69+
# Then, an admin would need to make "Check exercises (lts-A.B)" not required,
70+
# and "Check exercises (lts-C.D)" required.
71+
#
72+
# We don't want to ask for that much admin intervention.
73+
# So we write lts-from-exercises,
74+
# and an admin only needs to make "Check exercises (lts-from-exercises)" required.
75+
- lts-from-exercises
76+
- nightly
77+
# Discussion seems to indicate that continue-on-error is not exactly equivalent to Travis's allow_failures, but it's the best we can do for now.
78+
# If failures on nightly cause too much confusion in CI status,
79+
# consider just not running against the nightly resolver.
80+
# https://github.com/actions/toolkit/issues/399
81+
continue-on-error: ${{ matrix.resolver == 'nightly' }}
82+
steps:
83+
- uses: actions/checkout@v2
84+
85+
- name: Install yq (for stack resolvers)
86+
run: |
87+
sudo add-apt-repository -y ppa:rmescandon/yq
88+
sudo apt-get -q update
89+
sudo apt-get -y install yq
90+
91+
- name: Resolve resolver
92+
id: resolve-resolver
93+
run: |
94+
# We need this in two places:
95+
# 1. cache key
96+
# 2. --resolver arg to installing hlint
97+
# It's unsafe to just use lts-from-exercises as the cache key,
98+
# because that would cause caches from differing LTSes to collide
99+
# (if we have updated the LTS version of all exercises between job runs).
100+
if [ "${{ matrix.resolver }}" = "lts-from-exercises" ]; then
101+
resolver=$(yq read "$(ls -1 exercises/*/stack.yaml | head -1)" resolver)
102+
else
103+
resolver=${{ matrix.resolver }}
104+
fi
105+
echo "::set-output name=resolver::$resolver"
106+
107+
- name: Week number
108+
id: week-no
109+
run: |
110+
# We use this to form part of the cache key.
111+
# This will get us to periodically rebuild from scratch,
112+
# just to make sure that everything still works from scratch.
113+
# Because we also have a weekly build at the start of every week,
114+
# most of the time caches should hit.
115+
echo "::set-output name=week-no::$(date -u +%Y-%U)"
116+
117+
- uses: actions/cache@v2
118+
id: cache
119+
with:
120+
path: |
121+
~/.stack
122+
~/.foldercache
123+
~/.local/bin/hlint
124+
key: ${{ steps.resolve-resolver.outputs.resolver }}-${{ steps.week-no.outputs.week-no }}-${{ hashFiles('exercises/**/package.yaml') }}
125+
restore-keys: ${{ steps.resolve-resolver.outputs.resolver }}-${{ steps.week-no.outputs.week-no }}-
126+
127+
- uses: actions/setup-haskell@v1
128+
with:
129+
enable-stack: true
130+
stack-version: 'latest'
131+
132+
- name: Install hlint
133+
run: |
134+
if [ -x ${HOME}/.local/bin/hlint ]; then
135+
echo already installed
136+
else
137+
stack --resolver ${{ steps.resolve-resolver.outputs.resolver }} install hlint
138+
fi
139+
${HOME}/.local/bin/hlint --version
140+
141+
- name: HLint
142+
run: ${HOME}/.local/bin/hlint ${{ github.workspace }}
143+
144+
- name: Check exercises
145+
run: |
146+
# Explicit set exercises' resolver only if it's not the current one.
147+
if [ "${{ matrix.resolver }}" != "lts-from-exercises" ]; then
148+
export SET_RESOLVER="--resolver ${{ matrix.resolver }}"
149+
fi
150+
151+
for exercise in ${{ github.workspace }}/exercises/*/; do
152+
time bin/test-stub $exercise
153+
time bin/test-all-examples $exercise
154+
done

.travis.yml

+2-63
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,6 @@
11
sudo: false # Use the container-based infrastructure.
22
language: bash
3-
4-
addons:
5-
apt:
6-
packages:
7-
- libgmp-dev # Stack's GHC depends on this.
8-
9-
cache:
10-
timeout: 600 # The cache is too big to upload in 180 seconds.
11-
directories:
12-
- $HOME/.stack # Global stack's cache.
13-
- $HOME/.foldercache # Per exercise `.stack-work` cache.
14-
15-
env:
16-
- RESOLVER="lts-16.21" CURRENT="YES" # Equal to each stack.yaml.
17-
- RESOLVER="nightly" # Latest nightly snapshot.
18-
19-
matrix:
20-
allow_failures: # The snapshot `nightly` is just an alias to
21-
- env: RESOLVER="nightly" # the newest version released. We don't want
22-
fast_finish: true # Travis to fail on new incompatible releases.
23-
24-
before_install:
25-
- mkdir -p ${HOME}/bin # Create folder for stack.
26-
- export PATH="${HOME}/bin:$PATH" # For stack
27-
- export PATH="${TRAVIS_BUILD_DIR}/bin:$PATH" # For {,fetch-}configlet.
28-
- sudo add-apt-repository -y ppa:rmescandon/yq
29-
- sudo apt-get -q update
30-
- sudo apt-get -y install yq
31-
323
install:
33-
- travis_retry fetch-configlet
34-
- travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 -o pack.tgz
35-
- tar xzf pack.tgz --wildcards --strip-components=1 -C ${HOME}/bin '*/stack'
36-
- stack --resolver ${RESOLVER} --install-ghc install hlint
37-
- stack --version
38-
4+
- /bin/true
395
script:
40-
- "sh ./bin/ensure-readmes-are-updated.sh"
41-
- "sh ./bin/check-configlet-fmt.sh"
42-
- "sh ./bin/ensure-stack-resolvers-synced.sh"
43-
- |
44-
# Check for invalid UUIDs.
45-
# can be removed once `configlet lint` gains this ability.
46-
# Check issue https://github.com/exercism/configlet/issues/99
47-
bad_uuid=$(jq --raw-output '.exercises | map(.uuid) | .[]' config.json | grep -vE '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$')
48-
if [ -n "$bad_uuid" ]; then
49-
echo "invalid UUIDs found! please correct these to be valid UUIDs:"
50-
echo "$bad_uuid"
51-
exit 1
52-
fi
53-
- |
54-
set -e
55-
56-
configlet lint . # Check basic track configuration.
57-
hlint ${TRAVIS_BUILD_DIR} # Run `hlint` on the entire repository.
58-
59-
# Explicit set exercises' resolver only if it's not the current one.
60-
if [ "${CURRENT}" != "YES" ]; then
61-
export SET_RESOLVER="--resolver ${RESOLVER}"
62-
fi
63-
64-
for exercise in ${TRAVIS_BUILD_DIR}/exercises/*/ ; do
65-
time bin/test-stub $exercise
66-
time bin/test-all-examples $exercise
67-
done
6+
- /bin/true

README.md

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Exercism Haskell Track
22

3-
[![Build Status](https://travis-ci.org/exercism/haskell.png?branch=master)](https://travis-ci.org/exercism/haskell)
3+
[![Tests](https://github.com/exercism/haskell/workflows/Tests/badge.svg?branch=master)](https://github.com/exercism/haskell/actions?query=workflow%3ATests+branch%3Amaster)
44

55
Exercism exercises in Haskell
66

@@ -65,7 +65,9 @@ The [track anatomy documentation](https://github.com/exercism/docs/blob/master/l
6565
### Directory structure
6666
```bash
6767
├── .gitignore
68-
├── .travis.yml
68+
├── .github
69+
│ └── workflows
70+
│ └── tests.yml
6971
├── LICENSE
7072
├── README.md
7173
├── bin
@@ -139,7 +141,7 @@ The stub solution should be as general as possible in order to not exclude any p
139141

140142
The stub solution must compile by itself (with `stack build`).
141143
Ideally, it would also compile together with the test suite (with `stack test --no-run-tests`).
142-
These two conditions are enforced by Travis.
144+
These two conditions are enforced by GitHub Actions CI.
143145
If the second condition cannot be met for a good reason, place the explanation in `.meta/DONT-TEST-STUB` to circumvent the check.
144146
The first condition is always enforced and cannot be circumvented.
145147

@@ -166,23 +168,23 @@ These example types were proposed and accepted in https://github.com/exercism/ha
166168
The test suite should be derived from the respective `problem-specifications/exercises/<exercise-name>/canonical-data.json` and comply to some formatting and coding standards (to get an idea you may look at some of the existing tests).
167169

168170
## Running Tests
169-
In order to be accepted by Travis-CI, every exercise must be registered in
171+
In order to be accepted by GitHub Actions, every exercise must be registered in
170172
`config.json`, it must compile without warnings and the example solution must
171173
pass the tests without failures. Additionally the tests should not run longer than
172174
a few seconds.
173175

174176
First you need to provide an [example solution](#example-solution).
175177

176178
We provide three scripts in the `bin` directory of this repository to run the tests.
177-
These are the same scripts as those used by Travis CI.
179+
These are the same scripts as those used by GitHub Actions.
178180

179181
* `test-example path/to/example/dir` runs the tests on a single example.
180182
* `test-all-examples path/to/exercise/dir` runs the tests on all examples for an exercise.
181183
* `test-stub path/to/exercise/dir` checks that the stub for the given exercise compiles.
182184

183185
## Running HLint
184186
All code in this repository should be as idiomatic as possible, so we
185-
enforce in Travis-CI that it returns `No hints` when processed by
187+
enforce in GitHub Actions that it returns `No hints` when processed by
186188
HLint.
187189

188190
It is highly recommended to run `hlint` on your sources before opening a

bin/ensure-readmes-are-updated.sh

+2-5
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
override_message="I have confirmed that no README check is needed"
44

55
# If *any* commit message contains the override message:
6-
# (Note that TRAVIS_COMMIT_MESSAGE for PRs is usually "merge commit abc123 into def456"
7-
# which is not useful. Just search all commit messages between master and HEAD)
8-
9-
if git log master..HEAD | grep -q "$override_message"; then
6+
if git log origin/master..HEAD | grep -q "$override_message"; then
107
echo "WARNING: You've overridden the README check, which applies to ALL commits in this PR."
118
echo "No README in this PR will be checked for changes."
129
exit 0
@@ -30,7 +27,7 @@ newline=$'\n '
3027

3128
missing_readmes=""
3229
wrong_readmes=""
33-
for exercise in $(git diff --name-only master..$(git rev-parse --abbrev-ref HEAD) | grep exercises/ | cut -d'/' -f2 -s | sort -fu); do
30+
for exercise in $(git diff --name-only origin/master..$(git rev-parse --abbrev-ref HEAD) | grep exercises/ | cut -d'/' -f2 -s | sort -fu); do
3431
echo "Checking readme for $exercise"
3532
readme_path="exercises/${exercise}/README.md"
3633
if [ ! -f $readme_path ]; then

bin/ensure-stack-resolvers-synced.sh

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
# This ensures that each exercise's stack resolver has the same version.
44

55
differing_stack=""
6-
expected_stack=$(grep 'RESOLVER.*CURRENT' .travis.yml | head -1 | cut -d'"' -f2)
6+
first_stack_yaml=$(ls -1 exercises/*/stack.yaml | head -1)
7+
expected_stack=$(yq read "$first_stack_yaml" resolver)
78
echo "All exercises should have resolver $expected_stack"
8-
for exercise in ${TRAVIS_BUILD_DIR}/exercises/*/ ; do
9+
for exercise in $(git rev-parse --show-toplevel)/exercises/*/ ; do
910
exercise_stack=$(yq read "$exercise/stack.yaml" resolver)
1011
if ! [ "$exercise_stack" = "$expected_stack" ]; then
1112
differing_stack="$differing_stack $(basename "$exercise")"

bin/test-example

+3-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ fi
3434

3535
cd $buildfolder
3636

37-
if [ -n "$TRAVIS" ]; then
37+
if [ -n "$GITHUB_ACTIONS" ]; then
3838
cachedir="$HOME"
3939
else
4040
cachedir="$xhaskell"
@@ -46,7 +46,7 @@ ln -f -s "$examplecache"
4646
exampletype=$(echo "$examplename" | cut -d- -f1)
4747

4848
runstack () {
49-
# SET_RESOLVER passed by .travis.yml - sets --resolver if not current.
49+
# SET_RESOLVER passed by GitHub Actions - sets --resolver if not current.
5050
stack "$@" ${SET_RESOLVER} `# Select the correct resolver. `\
5151
--install-ghc `# Download GHC if not in cache.`\
5252
--no-terminal `# Terminal detection is broken.`\
@@ -55,7 +55,7 @@ runstack () {
5555
# --no-run-benchmarks `# do not run them. `
5656
#
5757
# We are temporarily disabling the benchmarks
58-
# to speed up Travis-CI and try to stay below
58+
# to speed up CI and try to stay below
5959
# the 50 minutes limit.
6060
}
6161

bin/test-stub

+3-3
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ fi
3333

3434
cd $buildfolder
3535

36-
if [ -n "$TRAVIS" ]; then
36+
if [ -n "$GITHUB_ACTIONS" ]; then
3737
cachedir="$HOME"
3838
else
3939
cachedir="$xhaskell"
@@ -42,7 +42,7 @@ examplecache="${cachedir}/.foldercache/${exercisename}/${examplename}/.stack-wor
4242
mkdir -p "$examplecache"
4343
ln -f -s "$examplecache"
4444

45-
# SET_RESOLVER passed by .travis.yml - sets --resolver if not current.
45+
# SET_RESOLVER passed by GitHub Actions - sets --resolver if not current.
4646
if [ -f "${exercisedir}/.meta/DONT-TEST-STUB" ]; then
4747
echo "only building stub"
4848
stack build ${SET_RESOLVER} --install-ghc --no-terminal
@@ -53,6 +53,6 @@ else
5353
# --bench --no-run-benchmarks
5454
#
5555
# We are temporarily disabling the benchmarks
56-
# to speed up Travis-CI and try to stay below
56+
# to speed up CI and try to stay below
5757
# the 50 minutes limit.
5858
fi

0 commit comments

Comments
 (0)