Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions .docker/production/Dockerfile.gha
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
############################################
### Base image ###
############################################

# Taken from .ruby-version
ARG RUBY_VERSION=2.6.3
FROM ruby:$RUBY_VERSION-slim-buster AS base
LABEL author="DCHBX"

ARG DEBIAN_FRONTEND=noninteractive

# Taken from Gemfile.lock
ARG BUNDLER_VERSION=2.4.22
ENV BUNDLER_VERSION=$BUNDLER_VERSION

ARG NODE_MAJOR=20
ENV NODE_MAJOR=$NODE_MAJOR

RUN apt-get update -qq && \
apt-get install -yq --no-install-recommends \
curl \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& truncate -s 0 /var/log/*log

# Add NodeJS to sources list
RUN curl -fsSL https://deb.nodesource.com/setup_$NODE_MAJOR.x | bash -

# Basic packages needed because we're using the slim image
RUN apt-get update \
&& apt-get -yq dist-upgrade \
&& apt-get install -y \
fontconfig \
libcurl4-openssl-dev \
libffi-dev \
libsodium23 \
libxext6 \
libxrender1 \
libyaml-cpp-dev \
nodejs \
default-jre \
openssl \
sshpass \
unzip \
zip \
zlib1g \
libjemalloc2 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& truncate -s 0 /var/log/*log

# Configure bundler and PATH, install bundler version
ENV GEM_HOME=/usr/local/bundle
ENV BUNDLE_PATH=$GEM_HOME
ENV BUNDLE_APP_CONFIG=$BUNDLE_PATH
ENV BUNDLE_BIN=/usr/local/bin
ENV BUNDLE_JOBS=4
ENV BUNDLE_RETRY=3

ENV LANG=C.UTF-8

ENV HOME=/cartafact

ENV PATH=$HOME/bin:$BUNDLE_BIN:$GEM_HOME/gems/bin:$PATH

# rubygems-update > 3.4.22 requires ruby >= 3.0.0
RUN gem update --system 3.4.22\
&& rm -f /usr/local/bin/ruby/gems/*/specifications/default/bundler-*.gemspec \
&& gem install bundler -v $BUNDLER_VERSION

RUN groupadd --gid 1001 nonroot \
&& useradd --uid 1001 --gid nonroot --shell /bin/bash --create-home nonroot

RUN mkdir $HOME \
&& chown -R nonroot:nonroot $HOME

# Configure app home directory
WORKDIR $HOME


###############################################################################
### Builder. Adds node and Yarn. Not necessary in production. ###
###############################################################################

FROM base AS builder

ARG DEBIAN_FRONTEND=noninteractive

# Add Yarn to the sources list
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
&& echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

# Install Nodejs & Yarn
RUN apt-get update -qq \
&& apt-get install -yq --no-install-recommends \
build-essential \
git \
libpq-dev \
yarn \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& truncate -s 0 /var/log/*log


########################################################
### Node and Bundle for production ###
########################################################

FROM builder AS prod_gems_and_assets

ENV RAILS_ENV=production

ARG HOSTNAME=localhost

COPY --chown=nonroot:nonroot ./Gemfile $HOME/Gemfile
COPY --chown=nonroot:nonroot ./Gemfile.lock $HOME/Gemfile.lock

RUN bundle config set --local without 'development test' \
&& bundle install

COPY --chown=nonroot:nonroot . $HOME

# https://github.com/rubygems/rubygems/issues/3225
RUN rm -rf $GEM_HOME/bundle/ruby/*/cache

################################################################
### Deployable image ###
################################################################

FROM base AS deploy

# Copy prebuilt gems
COPY --chown=nonroot:nonroot --from=prod_gems_and_assets $BUNDLE_PATH $BUNDLE_PATH

# Copy all app code again (sans gems, node_modules, assets)
COPY --chown=nonroot:nonroot . $HOME

USER nonroot

ENV RAILS_ENV=production

ENTRYPOINT ["bin/docker-entrypoint"]
23 changes: 23 additions & 0 deletions .github/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Release notes template (https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes)

changelog:
categories:
- title: Breaking Changes 🚧
labels:
# Semver-Major
- breaking change
- title: New Features ✨
labels:
# Semver-Minor
- enhancement
- title: Bug Fixes 🐛
labels:
# Semver-Patch
- bugfix
- title: Other Changes 📦
labels:
- "*"
exclude:
labels:
- chore
- version bump
214 changes: 214 additions & 0 deletions .github/workflows/build_and_deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
name: Build Image and Deploy

on:
workflow_dispatch:
push:
branches:
- "trunk"
- 'upgrade_ruby_and_rails'
- "build_changes"
pull_request:
branches:
- "trunk"
- 'upgrade_ruby_and_rails'
- "build_changes"

concurrency:
group: docker-${{ github.ref }}
cancel-in-progress: true

jobs:
prep:
runs-on: ubuntu-latest
outputs:
taggedImage: ${{ steps.prep.outputs.tagged_image }}
registryGhcr: ${{ steps.prep.outputs.registry_ghcr }}
shortSha: ${{ steps.prep.outputs.short_sha }}
branchName: ${{ steps.prep.outputs.branch_name }}
latestTag: ${{ steps.prep.outputs.latest_tag }}
repositoryName: ${{ steps.prep.outputs.repository_name }}
registryEcr: ${{ steps.prep.outputs.registry_ecr }}
steps:
- name: Prepare info
id: prep
run: |
SHORT_SHA=$(echo $GITHUB_SHA | head -c7)
REPO=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')
BRANCH_NAME="${{ github.head_ref || github.ref_name }}"
TAG=${BRANCH_NAME}-$(echo $GITHUB_SHA | head -c7)
IMAGE=ideacrew/$REPO
echo "tagged_image=${IMAGE}:${TAG}" >> $GITHUB_OUTPUT
echo "registry_ghcr=ghcr.io" >> $GITHUB_OUTPUT
echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT
echo "branch_name=${BRANCH_NAME}" >> $GITHUB_OUTPUT
echo "repository_name=${REPO}" >> $GITHUB_OUTPUT
echo "latest_tag=${IMAGE}:latest" >> $GITHUB_OUTPUT
echo "registry_ecr=public.ecr.aws" >> $GITHUB_OUTPUT

# Uses buildx to build and push the image
build-and-upload-image:
needs: [prep]
runs-on: ubuntu-latest
services:
mongo:
image: mongo:4.2
ports:
- 27017:27017
options: >-
--name "mongo"
--health-cmd mongo
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
# Check out repository
- uses: actions/checkout@v4

- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3
with:
install: true

- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
# Key is named differently to avoid collision
key: ${{ runner.os }}-multi-buildx-${{ github.ref }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-multi-buildx-${{ github.ref }}-
${{ runner.os }}-multi-buildx-

# Provide credentials for AWS
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v5
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1

# Must use docker login in order to specify public registry
- name: Login to Public ECR
uses: docker/login-action@v2
with:
registry: ${{ needs.prep.outputs.registryEcr }}
username: ${{ secrets.AWS_ACCESS_KEY_ID }}
password: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ${{ needs.prep.outputs.registryGhcr }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build Image
uses: docker/build-push-action@v6
with:
context: .
builder: ${{ steps.buildx.outputs.name }}
file: .docker/production/Dockerfile.gha
# Set the desired build target here
target: deploy
# needed to access mongo and rabbit on GHA machine
network: host
# send to public registry if not a pull request
push: ${{ github.event_name != 'pull_request' }}
# create local image (for scanning) if it is a pull request
load: ${{ github.event_name == 'pull_request' }}
tags: |
${{ format('{0}/{1}', needs.prep.outputs.registryGhcr, needs.prep.outputs.taggedImage) }}
${{ format('{0}/{1}', needs.prep.outputs.registryGhcr, needs.prep.outputs.latestTag) }}
${{ format('{0}/{1}', needs.prep.outputs.registryEcr, needs.prep.outputs.taggedImage) }}
${{ format('{0}/{1}', needs.prep.outputs.registryEcr, needs.prep.outputs.latestTag) }}
cache-from: type=local,src=/tmp/.buildx-cache
# Note the mode=max here
# More: https://github.com/moby/buildkit#--export-cache-options
# And: https://github.com/docker/buildx#--cache-tonametypetypekeyvalue
cache-to: type=local,mode=max,dest=/tmp/.buildx-cache-new
build-args: |
HOSTNAME=172.17.0.1
pull: true

- name: Scan Docker image
if: github.event_name != 'pull_request'
id: scan
uses: anchore/scan-action@v6
with:
image: ${{ format('{0}/{1}', needs.prep.outputs.registryGhcr, needs.prep.outputs.taggedImage) }}
acs-report-enable: true
fail-build: false
severity-cutoff: critical

# - name: upload Anchore scan SARIF report
# if: github.event_name != 'pull_request'
# uses: github/codeql-action/upload-sarif@v2
# with:
# sarif_file: ${{ steps.scan.outputs.sarif }}

- name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache

new-image-notification:
if: github.repository_owner == 'ideacrew' && github.event_name != 'pull_request'
needs: [prep, build-and-upload-image]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Git Commit Data w/ Message
uses: jcputney/git-commit-data-action@1.0.2
- name: Post to a Slack channel
id: ic-slack
uses: slackapi/slack-github-action@v1
with:
channel-id: 'docker-images-${{ needs.prep.outputs.repositoryName }}'
payload: |
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "New image built from <https://github.com/ideacrew/${{ needs.prep.outputs.repositoryName }}/commit/${{ needs.prep.outputs.shortSha }}|${{ needs.prep.outputs.shortSha }}> on `${{ needs.prep.outputs.branchName }}`"
}
}
]
}
env:
SLACK_BOT_TOKEN: ${{ secrets.YELLR_BOT_TOKEN }}


# enable slack when image is ready
notify-slack:
if: github.repository_owner == 'ideacrew' && github.event_name != 'pull_request'
needs: [new-image-notification, prep, build-and-upload-image]
runs-on: ubuntu-latest
strategy:
matrix:
registry: ['public.ecr.aws', 'ghcr.io']
steps:
- name: Post to a Slack channel
id: ic-slack
uses: slackapi/slack-github-action@v1
with:
channel-id: 'docker-images-${{ needs.prep.outputs.repositoryName }}'
payload: |
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*${{ matrix.registry }} image*:\n`${{ format('{0}/{1}', matrix.registry, needs.prep.outputs.taggedImage) }}`"
}
},
{
"type": "divider"
}
]
}
env:
SLACK_BOT_TOKEN: ${{ secrets.YELLR_BOT_TOKEN }}
Loading
Loading