Flee from Travis

Ondřej Popelka
5 min readFeb 3, 2021

Travis CI became a huge PITA last year. Basically, it randomly fails for hours or sometimes days. There are no status updates, no communication from support, etc. This is for another post.

Yesterday this hit me on a hugely legacy app, which has a pipeline split in 7 stages of building some docker images and executing over 350 functional tests. It’s a beast which takes slightly over an hour to build and unfortunately relies on Travis build cache which broke yesterday:

sudo: requiredlanguage: php
php: 5.6
cache:
directories:
- vendor
jobs:
include:
- stage: prepare
script:
- composer install --no-scripts
- ./vendor/bin/phpcs --standard=psr2 --ignore=vendor -n .
- docker info
- stage: tests
name: "Base Tests"
script:
- ./vendor/bin/phpunit --testsuite base-tests
- stage: tests
name: "Runner Tests"
script:
- ./vendor/bin/phpunit --testsuite runner-tests
- stage: tests
name: "Executor Tests"
script:
- ./vendor/bin/phpunit --testsuite executor-tests
- stage: tests
name: "S3 Tests"
script:
- ./vendor/bin/phpunit --testsuite s3-tests
- stage: tests
name: "ABS Tests"
script:
- export STORAGE_API_TOKEN=$STORAGE_API_TOKEN_ABS; ./vendor/bin/phpunit --testsuite abs-tests
- stage: tests
name: "Synapse Tests"
script:
- export STORAGE_API_TOKEN=$STORAGE_API_TOKEN_ABS; ./vendor/bin/phpunit --testsuite synapse-tests

Piece of Cake

Looking at the pipeline — it is actually very simple, since most of the stuff is wrapped in test suites. So instead of trying to fix the Travis cache (I opened a support ticket just for fun, but I don’t really expect any answer to that) I went ahead and rewrote went to move it to Azure Pipelines.

I also took the opportunity to make it more portable, so I further wrapped the tests in a docker image using a simple docker-compose.yml:

version: '3'
# for development purposes only
services:
tests: &tests
build:
context: .
dockerfile: DockerfileTests
image: keboola/docker-runner-tests
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /tmp/:/tmp/
environment:
... snip ...

Rewriting the .travis.yml to azure-pipelines.yml is a piece of cake then:

pr: none
trigger:
batch: true
branches:
include:
- '*'
tags:
include:
- '*'
pool:
vmImage: ubuntu-16.04
stages:
- stage: beforeTests
displayName: 'Before Tests'
jobs:
- job: prepare
displayName: Prepare
steps:
- script: |
docker-compose build
docker-compose run tests ./vendor/bin/phpcs --standard=psr2 --ignore=vendor -v -n .
displayName: 'PHP CS'
- stage: Tests
dependsOn: beforeTests
jobs:
- job: baseTests
displayName: 'Base Tests'
timeoutInMinutes: 120
steps:
- script: |
ip -4 addr show
docker-compose run tests ./vendor/bin/phpunit --testsuite base-tests --debug
env:
... snip ...
- job: runnerTests
displayName: 'Runner Tests'
timeoutInMinutes: 120
steps:
- script: |
docker-compose run tests ./vendor/bin/phpunit --testsuite runner-tests --debug
env:
... snip ...
- job: executorTests
displayName: 'Executor Tests'
timeoutInMinutes: 120
steps:
- script: |
docker-compose run tests ./vendor/bin/phpunit --testsuite executor-tests --debug
env:
... snip ...
- job: s3Tests
displayName: 'S3 Tests'
steps:
- script: |
docker-compose run tests ./vendor/bin/phpunit --testsuite s3-tests --debug
env:
... snip ...
- job: absTests
displayName: 'ABS Tests'
steps:
- script: |
docker-compose run -e STORAGE_API_TOKEN=$STORAGE_API_TOKEN_ABS tests ./vendor/bin/phpunit --testsuite abs-tests --debug
env:
... snip ...
- job: synapseTests
displayName: 'Synapse Tests'
steps:
- script: |
docker-compose run -e STORAGE_API_TOKEN=$STORAGE_API_TOKEN_ABS tests ./vendor/bin/phpunit --testsuite synapse-tests --debug
env:
... snip ...

This is hugely inefficient, because it builds the docker image in every stage — but that takes only a minute or so. Since the entire pipeline runs for an hour, I just don’t care.

Headache

Now comes the hard part — environment variables. The thing requires 22 environment variables, most of which are encrypted. I should of course go and recreate all the credentials, but that’s a day work. There is cheat though — Travis debug build in which you can list and copy all the environment variables. Hoorayyyy. You just click the debug button and SSH to the job.

Debug button (https://docs.travis-ci.com/user/running-build-in-debug-mode/)

Except.

The button is not there.

So if it is not there, you’re supposed to do it via the API. There is pretty simple request to do so:

curl --location --request POST 'https://api.travis-ci.com/job/XXXXX/debug' \
--header 'Authorization: token YYYY' \
--header 'Travis-API-Version: 3' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{"quiet": true}'

Where XXXX is job id (not build id) and token is Travis token found in account settings. Except that the token does not work (refreshing does not help). It simply returns:

access_denied

The API doesn’t even bother to return a JSON, so it must be really denied. An alternative way to obtain the token is though the travis CLI. So:

docker run -i -t --entrypoint=/bin/bash ruby
gem install travis
travis login --pro

And:

Not found. Which actually turns out to be a known issue. That has a workaround — login in to GitHub, generate a token a feed that into travis. Then you can use travis show to get the job id. Which doesn’t work either, because it stopped updating few months ago:

But luckily, the job id is in the URL at Travis page as well:

So got that, got a new token and I can now run the request. Aaand it returns:

{
"@type": "error",
"error_type": "wrong_credentials",
"error_message": "access denied"
}

Which is actually an improvement, because it means that the debug builds are not enabled for my repository. Which can happen either if the repository is broken or if it is public. In that case there are two options — write to support (don’t bother, no one replies) or make the repository private (or private → public → private) and synchronize the accounts:

Synchronize account

And then it works, the debug build starts, I can get the environments and fill them in Azure Pipeline and be minus one repository on Travis.

Obviously all this I could’ve known ahead If only not following the official manual, because other people already went through this.

--

--