travis: check spelling of binaries and manpages, use docker

We now build a docker base container based on debian sid (where the very
latest packages are available). That base container is updated once a
month, or whenever travis-build.Dockerfile or debian/control change, but
re-used for subsequent travis runs. While the initial build might take
up to 15 minutes, subsequent builds typically run in a minute or two.

All the different steps that we run on travis are now factored into
separate scripts in the travis/ directory.

Switching to docker should also help with issue #2174.
This commit is contained in:
Michael Stapelberg 2016-02-01 09:42:55 +01:00
parent 065ce6b8fc
commit fbfbdb8e12
16 changed files with 164 additions and 50 deletions

View File

@ -1,49 +1,23 @@
sudo: required sudo: required
dist: trusty dist: trusty
services:
- docker
language: c language: c
compiler: compiler:
- gcc - gcc
- clang - clang
addons: env:
apt: global:
sources: - BASENAME="i3wm/travis-base:$(date +'%Y-%m')-$(./travis/ha.sh)"
- ubuntu-toolchain-r-test - secure: "B5IICA8MPx/FKaB50rTPqL8P1NU+Q0yuWl+lElL4+a9xSyLikfm3NzUPHoVwx8lNw2AVK6br7p0OmF7vMFjqAgrgc1cajTtEae5uFRKNUrWLpXM046YgNEYLLIHsQOjInxE+S4O6EFVzsUqsu8aeo2Xhq4sm4iUocG7e5isYgYo=" # DOCKER_PASS
packages: - secure: "EIvrq8PG7lRjidppG0RCv4F0X4GP3DT9F5+ixVuGPfhK/hZT3jYC2AVY9G+NnUcXVwQEpW92rlqpftQ/qZ13FoyWokC8ZyoyD06fr5FPCfoFF3OczZwAJzZYkObI/hE9+/hXcylx/Os6N4INd2My1ntGk3JPsWL9riopod5EjSg=" # DOCKER_EMAIL
- clang-format-3.5 - secure: "hvhBunS4xXTgnIOsk/BPT7I7FrJhvVwCSt5PfxxvMqNaztOJI9BuK7ZrZ5Cy38KyHwlh3VHAH5AaCygJcPauoSQCV3bpnlbaWn3ruq2F0Q697Q5uNf73liXzyUqb9/Zvfvge4y4WWOhP5tVz1C6ZBe/NfhU7pqKLMA+6ads+99c=" # DOCKER_USER
- libllvm3.5
- clang-3.5
- gcc-5
before_install:
- sudo DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-suggests --no-install-recommends devscripts equivs xdotool
install: install:
- sudo DEBIAN_FRONTEND=noninteractive mk-build-deps --install --remove --tool 'apt-get -yq --no-install-suggests --no-install-recommends' debian/control
# Install as many dependencies as possible via apt because cpanm is not very reliable/easy to debug.
- sudo DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-suggests --no-install-recommends libanyevent-perl libanyevent-i3-perl libextutils-pkgconfig-perl xcb-proto cpanminus xvfb xserver-xephyr xauth libinline-perl libxml-simple-perl libmouse-perl libmousex-nativetraits-perl libextutils-depends-perl perl-modules libtest-deep-perl libtest-exception-perl libxml-parser-perl libtest-simple-perl libtest-fatal-perl libdata-dump-perl libtest-differences-perl libxml-tokeparser-perl libtest-use-ok-perl libipc-run-perl libxcb-xtest0-dev
- sudo /bin/sh -c 'cpanm -n -v X11::XCB || true'
- sudo /bin/sh -c 'cpanm -n -v AnyEvent::I3 || true'
script:
- if [ -a .git/shallow ]; then git fetch --unshallow; fi - if [ -a .git/shallow ]; then git fetch --unshallow; fi
- if [ "$CC" = "clang" ]; then export CC="clang-3.5"; fi - docker pull ${BASENAME} || ./travis/docker-build-and-push.sh
- if [ "$CC" = "gcc" ]; then export CC="gcc-5"; fi script:
- CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Werror" make -j ASAN=1 - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-safe-wrappers.sh
- (cd testcases && xvfb-run ./complete-run.pl --parallel=1 || (cat latest/complete-run.log; false)) - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-formatting.sh
- clang-format-3.5 -i $(find . -name "*.[ch]" | tr '\n' ' ') && git diff --exit-code || (echo 'Code was not formatted using clang-format!'; false) - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC -e CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Werror" ${BASENAME} make all mans -j ASAN=1
- | - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-spelling.pl
funcs='malloc|calloc|realloc|strdup|strndup|asprintf|write' - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/run-tests.sh
cstring='"([^"\\]|\\.)*"'
cchar="'[^\\\\]'|'\\\\.[^']*'"
regex="^([^'\"]|${cstring}|${cchar})*\<(${funcs})\>"
detected=0
while IFS= read -r file; do
if { cpp -w -fpreprocessed "$file" || exit "$?"; } | grep -E -- "$regex"; then
echo "^ $file calls a function that has a safe counterpart."
detected=1
fi
done << EOF
$(find -name '*.c' -not -name safewrappers.c -not -name strndup.c)
EOF
if [ "$detected" -ne 0 ]; then
echo
echo "Calls of functions that have safe counterparts were detected."
exit 1
fi

View File

@ -109,7 +109,7 @@ for my $line (@lines) {
# Second step: Generate the enum values for all states. # Second step: Generate the enum values for all states.
# It is important to keep the order the same, so we store the keys once. # It is important to keep the order the same, so we store the keys once.
# We sort descendingly by length to be able to replace occurences of the state # We sort descendingly by length to be able to replace occurrences of the state
# name even when one states name is included in another ones (like FOR_WINDOW # name even when one states name is included in another ones (like FOR_WINDOW
# is in FOR_WINDOW_COMMAND). # is in FOR_WINDOW_COMMAND).
my @keys = sort { (length($b) <=> length($a)) or ($a cmp $b) } keys %states; my @keys = sort { (length($b) <=> length($a)) or ($a cmp $b) } keys %states;

View File

@ -176,14 +176,14 @@ static int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_rel
static void finish_input() { static void finish_input() {
char *command = (char *)concat_strings(glyphs_utf8, input_position); char *command = (char *)concat_strings(glyphs_utf8, input_position);
/* count the occurences of %s in the string */ /* count the occurrences of %s in the string */
int c; int c;
int len = strlen(format); int len = strlen(format);
int cnt = 0; int cnt = 0;
for (c = 0; c < (len - 1); c++) for (c = 0; c < (len - 1); c++)
if (format[c] == '%' && format[c + 1] == 's') if (format[c] == '%' && format[c + 1] == 's')
cnt++; cnt++;
printf("occurences = %d\n", cnt); printf("occurrences = %d\n", cnt);
/* allocate space for the output */ /* allocate space for the output */
int inputlen = strlen(command); int inputlen = strlen(command);

View File

@ -513,7 +513,7 @@ typedef struct placeholder_t {
} placeholder_t; } placeholder_t;
/** /**
* Replaces occurences of the defined placeholders in the format string. * Replaces occurrences of the defined placeholders in the format string.
* *
*/ */
char *format_placeholders(char *format, placeholder_t *placeholders, int num); char *format_placeholders(char *format, placeholder_t *placeholders, int num);

View File

@ -16,7 +16,7 @@
#endif #endif
/* /*
* Replaces occurences of the defined placeholders in the format string. * Replaces occurrences of the defined placeholders in the format string.
* *
*/ */
char *format_placeholders(char *format, placeholder_t *placeholders, int num) { char *format_placeholders(char *format, placeholder_t *placeholders, int num) {

View File

@ -26,7 +26,7 @@ Specify the path to the i3 IPC socket (it should not be necessary to use this
option, i3-input will figure out the path on its own). option, i3-input will figure out the path on its own).
-F <format>:: -F <format>::
Every occurence of "%s" in the <format> string is replaced by the user input, Every occurrence of "%s" in the <format> string is replaced by the user input,
and the result is sent to i3 as a command. Default value is "%s". and the result is sent to i3 as a command. Default value is "%s".
-l <limit>:: -l <limit>::

View File

@ -89,7 +89,7 @@ i3-msg -t get_tree
=== I3SOCK === I3SOCK
If no ipc-socket is specified on the commandline, this variable is used If no ipc-socket is specified on the commandline, this variable is used
to determine the path, at wich the unix domain socket is expected, on which to determine the path, at which the unix domain socket is expected, on which
to connect to i3. to connect to i3.
== SEE ALSO == SEE ALSO

View File

@ -250,7 +250,7 @@ void cmd_criteria_match_windows(I3_CMD) {
DLOG("matches window!\n"); DLOG("matches window!\n");
accept_match = true; accept_match = true;
} else { } else {
DLOG("doesnt match\n"); DLOG("doesn't match\n");
FREE(current); FREE(current);
continue; continue;
} }

View File

@ -910,7 +910,7 @@ bool parse_file(const char *f, bool use_nagbar) {
FREE(bufcopy); FREE(bufcopy);
/* Then, allocate a new buffer and copy the file over to the new one, /* Then, allocate a new buffer and copy the file over to the new one,
* but replace occurences of our variables */ * but replace occurrences of our variables */
char *walk = buf, *destwalk; char *walk = buf, *destwalk;
char *new = smalloc(stbuf.st_size + extra_bytes + 1); char *new = smalloc(stbuf.st_size + extra_bytes + 1);
destwalk = new; destwalk = new;

29
travis-build.Dockerfile Normal file
View File

@ -0,0 +1,29 @@
# vim:ft=Dockerfile
FROM debian:sid
RUN echo force-unsafe-io > /etc/dpkg/dpkg.cfg.d/docker-apt-speedup
# Paper over occasional network flakiness of some mirrors.
RUN echo 'APT::Acquire::Retries "5";' > /etc/apt/apt.conf.d/80retry
# NOTE: I tried exclusively using gce_debian_mirror.storage.googleapis.com
# instead of httpredir.debian.org, but the results (Fetched 123 MB in 36s (3357
# kB/s)) are not any better than httpredir.debian.org (Fetched 123 MB in 34s
# (3608 kB/s)). Hence, lets stick with httpredir.debian.org (default) for now.
# Install mk-build-deps (for installing the i3 build dependencies),
# clang and clang-format-3.5 (for checking formatting and building with clang),
# lintian (for checking spelling errors),
# test suite dependencies (for running tests)
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
dpkg-dev devscripts git equivs \
clang clang-format-3.5 \
lintian \
libanyevent-perl libanyevent-i3-perl libextutils-pkgconfig-perl xcb-proto cpanminus xvfb xserver-xephyr xauth libinline-perl libinline-c-perl libxml-simple-perl libmouse-perl libmousex-nativetraits-perl libextutils-depends-perl perl-modules libtest-deep-perl libtest-exception-perl libxml-parser-perl libtest-simple-perl libtest-fatal-perl libdata-dump-perl libtest-differences-perl libxml-tokeparser-perl libipc-run-perl libxcb-xtest0-dev libx11-xcb-perl libanyevent-i3-perl && \
rm -rf /var/lib/apt/lists/*
# Install i3 build dependencies.
COPY debian/control /usr/src/i3-debian-packaging/control
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive mk-build-deps --install --remove --tool 'apt-get --no-install-recommends -y' /usr/src/i3-debian-packaging/control && \
rm -rf /var/lib/apt/lists/*

2
travis/check-formatting.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
clang-format-3.5 -i $(find . -name "*.[ch]" | tr '\n' ' ') && git diff --exit-code || (echo 'Code was not formatted using clang-format!'; false)

19
travis/check-safe-wrappers.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/sh
funcs='malloc|calloc|realloc|strdup|strndup|asprintf|write'
cstring='"([^"\\]|\\.)*"'
cchar="'[^\\\\]'|'\\\\.[^']*'"
regex="^([^'\"]|${cstring}|${cchar})*\<(${funcs})\>"
detected=0
while IFS= read -r file; do
if { cpp -w -fpreprocessed "$file" || exit "$?"; } | grep -E -- "$regex"; then
echo "^ $file calls a function that has a safe counterpart."
detected=1
fi
done << EOF
$(find -name '*.c' -not -name safewrappers.c -not -name strndup.c)
EOF
if [ "$detected" -ne 0 ]; then
echo
echo "Calls of functions that have safe counterparts were detected."
exit 1
fi

64
travis/check-spelling.pl Executable file
View File

@ -0,0 +1,64 @@
#!/usr/bin/env perl
# vim:ts=4:sw=4:expandtab
#
# © 2016 Michael Stapelberg
#
# Checks for spelling errors in binaries and manpages (to be run by continuous
# integration to point out spelling errors before accepting contributions).
use strict;
use warnings;
use v5.10;
use autodie;
use lib 'testcases/lib';
use i3test::Util qw(slurp);
use Lintian::Check qw(check_spelling);
# Lintian complains if we dont set a vendor.
use Lintian::Data;
use Lintian::Profile;
Lintian::Data->set_vendor(
Lintian::Profile->new('debian', ['/usr/share/lintian'], {}));
my $exitcode = 0;
# Whitelist for spelling errors in manpages, in case the spell checker has
# false-positives.
my $binary_spelling_exceptions = {
#'exmaple' => 1, # Example for how to add entries to this whitelist.
'betwen' => 1, # asan_flags.inc contains this spelling error.
};
my @binaries = qw(
i3
i3-config-wizard/i3-config-wizard
i3-dump-log/i3-dump-log
i3-input/i3-input
i3-msg/i3-msg
i3-nagbar/i3-nagbar
i3bar/i3bar
);
for my $binary (@binaries) {
check_spelling(slurp($binary), $binary_spelling_exceptions, sub {
my ($current, $fixed) = @_;
say STDERR qq|Binary "$binary" contains a spelling error: "$current" should be "$fixed"|;
$exitcode = 1;
});
}
# Whitelist for spelling errors in manpages, in case the spell checker has
# false-positives.
my $manpage_spelling_exceptions = {
};
for my $name (glob('man/*.1')) {
for my $line (split(/\n/, slurp($name))) {
next if $line =~ /^\.\\\"/o;
check_spelling($line, $manpage_spelling_exceptions, sub {
my ($current, $fixed) = @_;
say STDERR qq|Manpage "$name" contains a spelling error: "$current" should be "$fixed"|;
$exitcode = 1;
});
}
}
exit $exitcode;

11
travis/docker-build-and-push.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/sh
set -e
# .dockerignore is created on demand so that release.sh and other scripts are
# not influenced by our travis setup.
echo .git > .dockerignore
docker build --pull --no-cache --rm -t=${BASENAME} -f travis-build.Dockerfile .
docker login -e ${DOCKER_EMAIL} -u ${DOCKER_USER} -p ${DOCKER_PASS}
docker push ${BASENAME}

7
travis/ha.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/sh
# Returns a hash to be used as version number suffix for the i3/travis-base
# docker container. The hash is over all files which influence what gets
# installed in the container, so that any changes in what needs to be installed
# will result in a cache invalidation.
cat debian/control travis-build.Dockerfile | sha256sum | dd bs=1 count=8 status=none

8
travis/run-tests.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh
cd testcases
# Try running the tests in parallel so that the common case (tests pass) is
# quick, but fall back to running them in sequence to make debugging easier.
if ! xvfb-run ./complete-run.pl
then
xvfb-run ./complete-run.pl --parallel=1 || (cat latest/complete-run.log; false)
fi