From 366a0ffa67b021a7895fe47ebc355a34145528fb Mon Sep 17 00:00:00 2001 From: Nat Budin Date: Fri, 25 Aug 2023 14:03:39 -0700 Subject: [PATCH] CI setup --- .dockerignore | 31 ++++++++ .github/release-drafter.yml | 34 +++++++++ .github/workflows/ci.yml | 91 +++++++++++++++++++++++ .github/workflows/release.yml | 21 ++++++ .node-version | 1 + Dockerfile | 56 +++++++++++++++ Gemfile.lock | 132 ++++++++++++++++++---------------- config/database.yml.docker | 2 + config/initializers/devise.rb | 2 +- 9 files changed, 309 insertions(+), 61 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .node-version create mode 100644 Dockerfile create mode 100644 config/database.yml.docker diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e8f0c9c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,31 @@ +# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files. + +# Ignore git directory. +/.git/ + +# Ignore bundler config. +/.bundle + +# Ignore all default key files. +/config/master.key +/config/credentials/*.key + +# Ignore all environment files. +/.env* +!/.env.example + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* + +# Ignore storage (uploaded files in development and any SQLite databases). +/storage/* + +# Ignore assets. +/node_modules/ +/app/assets/builds/* +!/app/assets/builds/.keep +/public/assets + +# Ignore Dockerfile +Dockerfile diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..448b4b4 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,34 @@ +name-template: 'v$RESOLVED_VERSION' +tag-template: 'v$RESOLVED_VERSION' +categories: + - title: '🚀 Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + label: 'chore' + - title: '📦 Dependency updates' + labels: + - 'dependencies' +change-template: '- $TITLE @$AUTHOR (#$NUMBER)' +change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. +version-resolver: + major: + labels: + - 'major' + minor: + labels: + - 'minor' + patch: + labels: + - 'patch' + default: patch +template: | + ## Changes + + $CHANGES diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f30b475 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,91 @@ +name: Continuous integration + +on: + push: + branches: + - main + pull_request: {} + +jobs: + minitest: + name: minitest + runs-on: ubuntu-latest + env: + DATABASE_URL: postgres://postgres:postgres@localhost/${{ github.event.repository.name }}_test + RAILS_ENV: test + services: + postgres: + image: postgres:14.8 + env: + POSTGRES_PASSWORD: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + - name: install node + uses: actions/setup-node@v3 + - name: Database setup + run: bundle exec rake db:create db:schema:load + - name: Compile assets + run: bundle exec rake assets:precompile + - name: Run tests + run: TERM=xterm-color bundle exec rake test + - name: Publish Test Report + uses: mikepenz/action-junit-report@v3 + if: always() # always run even if the previous step fails + with: + check_name: "Minitest Report" + report_paths: "test/reports/TEST-*.xml" + - name: Archive HTML test reports + uses: actions/upload-artifact@v3 + if: always() + with: + name: test-reports + path: test/html_reports + docker-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Log in to registry + run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@master + - name: Read .ruby-version + id: ruby-version + run: echo "ruby-version=$(cat .ruby-version)" >> $GITHUB_OUTPUT + - name: Build (and push to registry, if on main) + uses: docker/build-push-action@v4 + with: + context: . + push: ${{ github.event_name == 'push' && github.event.ref == 'refs/heads/main' }} + platforms: linux/amd64 + tags: | + ghcr.io/${{ github.repository }}:${{ github.sha }} + build-args: | + RUBY_VERSION=${{ steps.ruby-version.outputs.ruby-version }} + update-release-draft: + runs-on: ubuntu-latest + if: github.event_name == 'push' && github.event.ref == 'refs/heads/main' + needs: + - docker-build + - minitest + outputs: + name: ${{ steps.release-drafter.outputs.name }} + html_url: ${{ steps.release-drafter.outputs.html_url }} + steps: + - uses: release-drafter/release-drafter@v5 + id: release-drafter + with: + config-name: release-drafter.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0e961f9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,21 @@ +name: Release + +on: + release: + types: [published] + +jobs: + docker-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Log in to registry + run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + - name: Pull built image + run: docker pull ghcr.io/${{ github.repository }}:${{ github.sha }} + - name: Tag image with version Tag + run: docker tag ghcr.io/${{ github.repository }}:${{ github.sha }} ghcr.io/${{ github.repository }}:${{ github.event.release.name }} + - name: Tag image as latest + run: docker tag ghcr.io/${{ github.repository }}:${{ github.sha }} ghcr.io/${{ github.repository }}:latest + - name: Push to registry + run: docker push ghcr.io/${{ github.repository }}:latest && docker push ghcr.io/${{ github.repository }}:${{ github.event.release.name }} diff --git a/.node-version b/.node-version new file mode 100644 index 0000000..99cdd80 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +16.15.0 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b4e9711 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,56 @@ +ARG RUBY_VERSION=2.7.6 +FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base + +# Rails app lives here +WORKDIR /rails + +# Set production environment +ENV RAILS_ENV="production" \ + BUNDLE_DEPLOYMENT="1" \ + BUNDLE_PATH="/usr/local/bundle" \ + BUNDLE_WITHOUT="development" + + +# Throw-away build stage to reduce size of final image +FROM base as build + +# Install packages needed to build gems +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y libpq-dev git build-essential nodejs npm + +# Install Yarn +RUN npm install -g yarn + +# Install application gems +COPY Gemfile Gemfile.lock .ruby-version ./ +RUN bundle install && \ + rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git + +# Copy application code +COPY . . + +# Move Docker database.yml into place +RUN mv config/database.yml.docker config/database.yml + +# Precompiling assets for production without requiring secret RAILS_MASTER_KEY +RUN SECRET_KEY_BASE_DUMMY=1 DATABASE_URL=postgres://dummy/dummy bundle exec rake assets:precompile + +# Final stage for app image +FROM base + +# Install packages needed for deployment +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y libpq5 nodejs && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# Copy built artifacts: gems, application +COPY --from=build /usr/local/bundle /usr/local/bundle +COPY --from=build /rails /rails + +# Run and own only the runtime files as a non-root user for security +RUN useradd rails --create-home --shell /bin/bash && \ + chown -R rails:rails /rails +USER rails:rails + +EXPOSE 3000 +CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"] diff --git a/Gemfile.lock b/Gemfile.lock index 59f5639..9622a84 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -30,39 +30,39 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (5.1.6.2) - actionpack (= 5.1.6.2) + actioncable (5.1.7) + actionpack (= 5.1.7) nio4r (~> 2.0) websocket-driver (~> 0.6.1) - actionmailer (5.1.6.2) - actionpack (= 5.1.6.2) - actionview (= 5.1.6.2) - activejob (= 5.1.6.2) + actionmailer (5.1.7) + actionpack (= 5.1.7) + actionview (= 5.1.7) + activejob (= 5.1.7) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.1.6.2) - actionview (= 5.1.6.2) - activesupport (= 5.1.6.2) + actionpack (5.1.7) + actionview (= 5.1.7) + activesupport (= 5.1.7) rack (~> 2.0) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.1.6.2) - activesupport (= 5.1.6.2) + actionview (5.1.7) + activesupport (= 5.1.7) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.1.6.2) - activesupport (= 5.1.6.2) + activejob (5.1.7) + activesupport (= 5.1.7) globalid (>= 0.3.6) - activemodel (5.1.6.2) - activesupport (= 5.1.6.2) - activerecord (5.1.6.2) - activemodel (= 5.1.6.2) - activesupport (= 5.1.6.2) + activemodel (5.1.7) + activesupport (= 5.1.7) + activerecord (5.1.7) + activemodel (= 5.1.7) + activesupport (= 5.1.7) arel (~> 8.0) - activesupport (5.1.6.2) + activesupport (5.1.7) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -113,8 +113,9 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.1.10) + concurrent-ruby (1.2.2) crass (1.0.6) + date (3.3.3) devise (4.7.1) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -126,15 +127,13 @@ GEM dotenv (= 2.2.1) railties (>= 3.2, < 5.2) ed25519 (1.3.0) - erubi (1.10.0) + erubi (1.12.0) erubis (2.7.0) execjs (2.7.0) - faraday (0.13.0) - multipart-post (>= 1.2, < 3) ffi (1.15.5) font-awesome-rails (4.7.0.2) railties (>= 3.2, < 5.2) - globalid (1.0.1) + globalid (1.1.0) activesupport (>= 5.0) haml (5.1.2) temple (>= 0.8.0) @@ -150,18 +149,21 @@ GEM listen (3.0.8) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) - loofah (2.19.1) + loofah (2.21.3) crass (~> 1.0.2) - nokogiri (>= 1.5.9) - mail (2.7.1) + nokogiri (>= 1.12.0) + mail (2.8.1) mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp method_source (0.8.2) mime-types (3.3.1) mime-types-data (~> 3.2015) mime-types-data (3.2020.1104) - mini_mime (1.0.1) - mini_portile2 (2.8.1) - minitest (5.17.0) + mini_mime (1.1.5) + mini_portile2 (2.8.4) + minitest (5.19.0) monetize (1.7.0) money (~> 6.9) money (6.9.0) @@ -172,13 +174,21 @@ GEM money (~> 6.9.0) railties (>= 3.0) multi_json (1.12.1) - multipart-post (2.0.0) + net-imap (0.3.7) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.1) + timeout net-scp (4.0.0) net-ssh (>= 2.6.5, < 8.0.0) + net-smtp (0.3.3) + net-protocol net-ssh (7.0.1) - nio4r (2.5.8) - nokogiri (1.14.3) - mini_portile2 (~> 2.8.0) + nio4r (2.5.9) + nokogiri (1.15.4) + mini_portile2 (~> 2.8.2) racc (~> 1.4) oj (3.3.5) orm_adapter (0.5.0) @@ -192,32 +202,34 @@ GEM pry (>= 0.10.4) puma (4.3.12) nio4r (~> 2.0) - racc (1.6.2) - rack (2.2.6.4) + racc (1.7.1) + rack (2.2.8) rack-cors (1.0.5) rack (>= 1.6.0) - rack-test (1.1.0) - rack (>= 1.0, < 3) - rails (5.1.6.2) - actioncable (= 5.1.6.2) - actionmailer (= 5.1.6.2) - actionpack (= 5.1.6.2) - actionview (= 5.1.6.2) - activejob (= 5.1.6.2) - activemodel (= 5.1.6.2) - activerecord (= 5.1.6.2) - activesupport (= 5.1.6.2) + rack-test (2.1.0) + rack (>= 1.3) + rails (5.1.7) + actioncable (= 5.1.7) + actionmailer (= 5.1.7) + actionpack (= 5.1.7) + actionview (= 5.1.7) + activejob (= 5.1.7) + activemodel (= 5.1.7) + activerecord (= 5.1.7) + activesupport (= 5.1.7) bundler (>= 1.3.0) - railties (= 5.1.6.2) + railties (= 5.1.7) sprockets-rails (>= 2.0.0) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.4.4) - loofah (~> 2.19, >= 2.19.1) - railties (5.1.6.2) - actionpack (= 5.1.6.2) - activesupport (= 5.1.6.2) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + railties (5.1.7) + actionpack (= 5.1.7) + activesupport (= 5.1.7) method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) @@ -263,7 +275,7 @@ GEM sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.2.1) + sprockets-rails (3.2.2) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) @@ -271,13 +283,13 @@ GEM net-scp (>= 1.1.2) net-ssh (>= 2.8.0) ssrf_filter (1.0.7) - stripe (3.3.0) - faraday (~> 0.9) + stripe (9.0.0) temple (0.8.2) - thor (1.1.0) + thor (1.2.2) thread_safe (0.3.6) tilt (2.0.10) - tzinfo (1.2.10) + timeout (0.4.0) + tzinfo (1.2.11) thread_safe (~> 0.1) uglifier (3.2.0) execjs (>= 0.3.0, < 3) diff --git a/config/database.yml.docker b/config/database.yml.docker new file mode 100644 index 0000000..89cb29d --- /dev/null +++ b/config/database.yml.docker @@ -0,0 +1,2 @@ +production: + url: <%= ENV.fetch('DATABASE_URL') %> diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 09efb17..cb4d090 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -6,7 +6,7 @@ # confirmation, reset password and unlock tokens in the database. # Devise will use the `secret_key_base` as its `secret_key` # by default. You can change it below and use your own secret key. - # config.secret_key = 'ac485247ec98a41f4fe5f43c4359d40e5d99c7403536e668c558b7abbdb76fd892117b0d5effb55c19304644003c2f24d30b4c40f67900ad30aa22c8845cb28a' + config.secret_key = ENV['DEVISE_SECRET_KEY'] || 'ac485247ec98a41f4fe5f43c4359d40e5d99c7403536e668c558b7abbdb76fd892117b0d5effb55c19304644003c2f24d30b4c40f67900ad30aa22c8845cb28a' # ==> Mailer Configuration # Configure the e-mail address which will be shown in Devise::Mailer,