2

The problem

I'm currently in the process of setting up Travis to run our tests and deploy if the tests are successfull. Which is pretty straight forward, but I don't want to deploy when these conditions are met:

 - The time is between 07:00 and 22:00 (workdays)
 - During the deploy the database has to migrate or elastic search has to index

Since a migration or an index can be quite expensive and means downtime. I'm using the heroku provider and using the on parameter is simple enough, however I'm struggling with the following situation:

 - Git push triggers new build
 - Travis correctly identifies that a deploy is not allowed
 - Deploy is skipped using the heroku provider `on` parameter
 - Build finishes

5 mins later

 - Git push triggers new build
 - Travis incorrectly identifies a deploy is allowed (this build doesn't need a migration/index but the previous build did)
 - Travis tries to deploy and production breaks

Wanted situation

So what I actually want is something like scheduled/delayed deployments. When Travis detects one of the expensive operations need to run it schedules a deployment and all subsequent builds skip deployment untill the deployment with the expensive scripts is done. That deploy should start automatically anywhere outside business hours.


I hope I've described it clearly, if any more information is needed please let me know!

Allard Stijnman
  • 1,284
  • 11
  • 22
  • I think you would have to use [`after_success`](https://docs.travis-ci.com/user/deployment/custom/) to trigger a script of your own making, Travis's `deploy` can only deal with relatively simple cases. – jonrsharpe Jun 08 '16 at 09:24
  • That doesn't help since that still doesn't preserve the state between builds. – Allard Stijnman Jun 08 '16 at 09:29
  • What state do you need to preserve? If you mean so that it just waits for the end of the working day, I think Travis will kill the build if it's idle for so long. – jonrsharpe Jun 08 '16 at 09:50
  • No, just so that subsequent builds that don't contain the migration/index change don't falsely trigger the deploy. So state as in an ENV var `NEED_MIGRATION` or something. And using a Cron Job to trigger a build outside of business hours. – Allard Stijnman Jun 08 '16 at 09:58
  • Maybe you need to decouple this from Travis - use an `after_success` to deploy to a staging environment with a cron job that periodically checks that 1. there's a new build; and 2. this is a good time to deploy it. – jonrsharpe Jun 08 '16 at 10:00
  • Yeah, a colleague of mine suggested something like that (but with an API in our app), but it just feels a little workaround/hacky to me. So I was looking for a clean solution in Travis. – Allard Stijnman Jun 08 '16 at 13:29

1 Answers1

1

Ok with the awesome help of the folks over at Travis I came up with the following solution:

  1. I've activated the CronJobs and set them to run daily. At the time of writing this you can't specify a time the crons run, instead they are scheduled around the time you create them. So create a cron in the middle of the night and it runs every night.

  2. Let git check if there were any changes in migration/index files over the last 24h, and if so skip the deploy.

  3. Check for the travis event type and only perform the index/migrations if it's the cron running.

  4. Profit.

The code

deploy:
  - provider: heroku
    skip_cleanup: true
    api_key: "${HEROKU_API_KEY}"
    app: cd-test
    on:
      # Condition is: cron job + migration and/or index necessary
      condition: "$TRAVIS_EVENT_TYPE == 'cron' && $(git log --since='yesterday 23:00' --format=oneline -- **/migrations/* **/search.py | wc -l) -gt 0"
    run:
      - "python manage.py migrate"
      - "python manage.py collectstatic --noinput"
      - "python manage.py index"

  - provider: heroku
    skip_cleanup: true
    api_key: "${HEROKU_API_KEY}"
    app: cd-test
    on:
      # Condition is: not cron job + no migration and/or index necessary
      condition: "$TRAVIS_EVENT_TYPE != 'cron' && $(git log --since='yesterday 23:00' --format=oneline -- **/migrations/* **/search.py | wc -l) == 0"
    run:
      - "python manage.py collectstatic --noinput"
Allard Stijnman
  • 1,284
  • 11
  • 22