Skip to content

Instantly share code, notes, and snippets.

@umonaca
Last active July 8, 2020 09:22
Show Gist options
  • Save umonaca/e864a896800f9af56057e8eb44ec478a to your computer and use it in GitHub Desktop.
Save umonaca/e864a896800f9af56057e8eb44ec478a to your computer and use it in GitHub Desktop.
Yet another simple CI/CD pipeline for mastodon using CircleCI
version: 2

aliases:
  - &defaults
    docker:
      - image: circleci/ruby:2.7-buster-node
        environment: &ruby_environment
          BUNDLE_JOBS: 3
          BUNDLE_RETRY: 3
          BUNDLE_APP_CONFIG: ./.bundle/
          BUNDLE_PATH: ./vendor/bundle/
          DB_HOST: localhost
          DB_USER: root
          RAILS_ENV: test
          ALLOW_NOPAM: true
          CONTINUOUS_INTEGRATION: true
          DISABLE_SIMPLECOV: true
          PAM_ENABLED: true
          PAM_DEFAULT_SERVICE: pam_test
          PAM_CONTROLLED_SERVICE: pam_test_controlled
    working_directory: ~/projects/mastodon/

  - &attach_workspace
    attach_workspace:
      at: ~/projects/

  - &persist_to_workspace
    persist_to_workspace:
      root: ~/projects/
      paths:
        - ./mastodon/

  - &restore_ruby_dependencies
    restore_cache:
      keys:
        - v3-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }}
        - v3-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-
        - v3-ruby-dependencies-

  - &install_steps
    steps:
      - checkout
      - *attach_workspace
      - restore_cache:
          keys:
            - v2-node-dependencies-{{ checksum "yarn.lock" }}
            - v2-node-dependencies-
      - run:
          name: Install yarn dependencies
          command: yarn install --frozen-lockfile
      - save_cache:
          key: v2-node-dependencies-{{ checksum "yarn.lock" }}
          paths:
            - ./node_modules/
      - *persist_to_workspace

  - &install_system_dependencies
      run:
        name: Install system dependencies
        command: |
          sudo apt-get update
          sudo apt-get install -y libicu-dev libidn11-dev libprotobuf-dev protobuf-compiler

  - &install_ruby_dependencies
      steps:
        - *attach_workspace
        - *install_system_dependencies
        - run:
            name: Set Ruby version
            command: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version
        - *restore_ruby_dependencies
        - run:
            name: Set bundler settings
            command: |
              bundle config clean 'true'
              bundle config deployment 'true'
              bundle config with 'pam_authentication'
              bundle config without 'development production'
              bundle config frozen 'true'
        - run:
            name: Install bundler dependencies
            command: bundle check || (bundle install && bundle clean)
        - save_cache:
            key: v3-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }}
            paths:
              - ./.bundle/
              - ./vendor/bundle/
        - persist_to_workspace:
            root: ~/projects/
            paths:
                - ./mastodon/.bundle/
                - ./mastodon/vendor/bundle/

  - &test_steps
      parallelism: 4
      steps:
        - *attach_workspace
        - *install_system_dependencies
        - run:
            name: Install FFMPEG
            command: sudo apt-get install -y ffmpeg
        - run:
            name: Load database schema
            command: ./bin/rails db:create db:schema:load db:seed
        - run:
            name: Run rspec in parallel
            command: |
              bundle exec rspec --profile 10 \
                                --format RspecJunitFormatter \
                                --out test_results/rspec.xml \
                                --format progress \
                                $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)
        - store_test_results:
            path: test_results
jobs:
  install:
    <<: *defaults
    <<: *install_steps

  install-ruby2.7:
    <<: *defaults
    <<: *install_ruby_dependencies

  install-ruby2.6:
    <<: *defaults
    docker:
      - image: circleci/ruby:2.6-buster-node
        environment: *ruby_environment
    <<: *install_ruby_dependencies

  build:
    <<: *defaults
    steps:
      - *attach_workspace
      - *install_system_dependencies
      - run:
          name: Precompile assets
          command: ./bin/rails assets:precompile
      - persist_to_workspace:
          root: ~/projects/
          paths:
              - ./mastodon/public/assets
              - ./mastodon/public/packs-test/

  test-migrations:
    <<: *defaults
    docker:
      - image: circleci/ruby:2.7-buster-node
        environment: *ruby_environment
      - image: circleci/postgres:12.2
        environment:
          POSTGRES_USER: root
          POSTGRES_HOST_AUTH_METHOD: trust
      - image: circleci/redis:5-alpine
    steps:
      - *attach_workspace
      - *install_system_dependencies
      - run:
          name: Create database
          command: ./bin/rails db:create
      - run:
          name: Run migrations
          command: ./bin/rails db:migrate

  test-ruby2.7:
    <<: *defaults
    docker:
      - image: circleci/ruby:2.7-buster-node
        environment: *ruby_environment
      - image: circleci/postgres:12.2
        environment:
          POSTGRES_USER: root
          POSTGRES_HOST_AUTH_METHOD: trust
      - image: circleci/redis:5-alpine
    <<: *test_steps

  test-ruby2.6:
    <<: *defaults
    docker:
      - image: circleci/ruby:2.6-buster-node
        environment: *ruby_environment
      - image: circleci/postgres:12.2
        environment:
          POSTGRES_USER: root
          POSTGRES_HOST_AUTH_METHOD: trust
      - image: circleci/redis:5-alpine
    <<: *test_steps

  test-webui:
    <<: *defaults
    docker:
      - image: circleci/node:12-buster
    steps:
      - *attach_workspace
      - run:
          name: Run jest
          command: yarn test:jest

  check-i18n:
    <<: *defaults
    steps:
      - *attach_workspace
      - *install_system_dependencies
      - run:
          name: Check locale file normalization
          command: bundle exec i18n-tasks check-normalized
      - run:
          name: Check for unused strings
          command: bundle exec i18n-tasks unused -l en
      - run:
          name: Check for wrong string interpolations
          command: bundle exec i18n-tasks check-consistent-interpolations
      - run:
          name: Check that all required locale files exist
          command: bundle exec rake repo:check_locales_files
  
  check-ruby-version:
    <<: *defaults
    steps:
      - checkout
      - run:
          name: Check if ruby version unchanged
          command: cat .ruby-version | egrep -q '2.6.6'

  check-node-version:
    <<: *defaults
    steps:
      - checkout
      - run:
          name: Check if node version unchanged
          command: cat .nvmrc | egrep -q '12'

  deploy:
    machine:
      enabled: true
    steps:
      - add_ssh_keys:
          fingerprints:
            - "SO:ME:FIN:G:ER:PR:IN:T"
      - run:
          name: Add known host
          command: mkdir -p ~/.ssh && ssh-keyscan YOUR_SERVER_IP >> ~/.ssh/known_hosts
      - run:
          name: Deploy source code over SSH
          command: ssh mastodon@YOUR_SERVER_IP "cd ~/live; git stash; git pull --no-edit origin master;"
      - run:
          name: Upgrade gems and js packages
          command: ssh mastodon@YOUR_SERVER_IP 'zsh -lc "set -e; cd ~/live;
            bundle install -j$(getconf _NPROCESSORS_ONLN); 
            yarn install --pure-lockfile"'
      - run:
          name: Database migration and precompile assets
          command: ssh mastodon@YOUR_SERVER_IP 'zsh -lc "set -e; cd ~/live; RAILS_ENV=production SKIP_POST_DEPLOYMENT_MIGRATIONS=true bundle exec rails db:migrate; 
            RAILS_ENV=production bundle exec rails assets:precompile"'
      - run:
          name: Reload and restart
          command: ssh mastodon@YOUR_SERVER_IP 'zsh -lc "set -e; sudo systemctl reload mastodon-web && sudo systemctl restart mastodon-{sidekiq,streaming}"'
      - run:
          name: Clean up
          command: ssh mastodon@YOUR_SERVER_IP 'zsh -lc "set -e; cd ~/live; RAILS_ENV=production bin/tootctl cache clear; 
            RAILS_ENV=production bundle exec rails db:migrate"'

workflows:
  version: 2
  build-test-and-deploy:
    jobs:
      - install
      - install-ruby2.7:
          requires:
            - install
      - install-ruby2.6:
          requires:
            - install
            - install-ruby2.7
      - build:
          requires:
            - install-ruby2.7
      - test-migrations:
          requires:
            - install-ruby2.7
      - test-ruby2.7:
          requires:
            - install-ruby2.7
            - build
      - test-ruby2.6:
          requires:
            - install-ruby2.6
            - build
      - test-webui:
          requires:
            - install
      - check-i18n:
          requires:
            - install-ruby2.7
      - check-ruby-version
      - check-node-version
      - deploy:
          requires:
            - check-ruby-version
            - test-migrations
            - test-webui
            - test-ruby2.7
            - check-i18n
          filters:
            branches:
              only: master
@umonaca
Copy link
Author

umonaca commented Jul 1, 2020

Note on concurrency and race conditions:

Since Free version or CircleCI only runs 1 job at a time their are no race conditions for the deployment job. If you are using other non-free plans, you can use this to ensure that only one deploy job runs at a time.

@umonaca
Copy link
Author

umonaca commented Jul 3, 2020

Update: I have fixed a missing step "Upgrade gems and js packages".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment