Skip to content

Commit

Permalink
Merge pull request #107 from goodeats/106-deployment
Browse files Browse the repository at this point in the history
deployment
  • Loading branch information
goodeats authored May 22, 2024
2 parents 4f0a301 + 8fc2f98 commit e8c1638
Show file tree
Hide file tree
Showing 13 changed files with 205 additions and 53 deletions.
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ SESSION_SECRET="super-duper-s3cret"
HONEYPOT_SECRET="super-duper-s3cret"
INTERNAL_COMMAND_TOKEN="some-made-up-token"
RESEND_API_KEY="re_blAh_blaHBlaHblahBLAhBlAh"
RESEND_EMAIL_ADDRESS="[email protected]"
SENTRY_DSN="your-dsn"

# the mocks and some code rely on these two being prefixed with "MOCK_"
Expand All @@ -15,3 +16,9 @@ GITHUB_CLIENT_SECRET="MOCK_GITHUB_CLIENT_SECRET"
GITHUB_TOKEN="MOCK_GITHUB_TOKEN"

FLY_APP_NAME="fly-app-name-1234"

# set this to false to prevent search engines from indexing the website
# default to allow indexing for seo safety
# set false for staging via fly secrets
# https://github.com/epicweb-dev/epic-stack/blob/main/docs/deployment.md
ALLOW_INDEXING="true"
88 changes: 43 additions & 45 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,48 +136,46 @@ jobs:
path: playwright-report/
retention-days: 30

# uncomment when I feel that fly deploy is stable enough
# deploy:
# name: 🚀 Deploy
# runs-on: ubuntu-22.04
# needs: [lint, typecheck, vitest, playwright]
# # only build/deploy main branch on pushes
# if:
# ${{ (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev') &&
# github.event_name == 'push' }}

# steps:
# - name: ⬇️ Checkout repo
# uses: actions/checkout@v3

# - name: 👀 Read app name
# uses: SebRollen/[email protected]
# id: app_name
# with:
# file: 'fly.toml'
# field: 'app'

# # move Dockerfile to root
# - name: 🚚 Move Dockerfile
# run: |
# mv ./other/Dockerfile ./Dockerfile
# mv ./other/.dockerignore ./.dockerignore

# - name: 🎈 Setup Fly
# uses: superfly/flyctl-actions/[email protected]

# - name: 🚀 Deploy Staging
# if: ${{ github.ref == 'refs/heads/dev' }}
# run:
# flyctl deploy --remote-only --build-arg COMMIT_SHA=${{ github.sha }}
# --app ${{ steps.app_name.outputs.value }}-staging
# env:
# FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

# - name: 🚀 Deploy Production
# if: ${{ github.ref == 'refs/heads/main' }}
# run:
# flyctl deploy --remote-only --build-arg COMMIT_SHA=${{ github.sha }}
# --build-secret SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
# env:
# FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
uncomment when I feel that fly deploy is stable enough
deploy:
name: 🚀 Deploy
runs-on: ubuntu-22.04
needs: [lint, typecheck, vitest, playwright]
# only build/deploy main branch on pushes
if: ${{ github.event_name == 'push' }}

steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4

- name: 👀 Read app name
uses: SebRollen/[email protected]
id: app_name
with:
file: 'fly.toml'
field: 'app'

# move Dockerfile to root
- name: 🚚 Move Dockerfile
run: |
mv ./other/Dockerfile ./Dockerfile
mv ./other/.dockerignore ./.dockerignore
- name: 🎈 Setup Fly
uses: superfly/flyctl-actions/[email protected]

- name: 🚀 Deploy Staging
if: ${{ github.ref == 'refs/heads/dev' }}
run:
flyctl deploy --remote-only --build-arg COMMIT_SHA=${{ github.sha }}
--app ${{ steps.app_name.outputs.value }}-staging
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

- name: 🚀 Deploy Production
if: ${{ github.ref == 'refs/heads/main' }}
run:
flyctl deploy --remote-only --build-arg COMMIT_SHA=${{ github.sha }}
--build-secret SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
15 changes: 13 additions & 2 deletions app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
where: { id: userId },
}),
{ timings, type: 'find user', desc: 'find user in root' },
)
)
: null
if (userId && !user) {
console.info('something weird happened')
Expand Down Expand Up @@ -179,11 +179,13 @@ function Document({
children,
nonce,
theme = 'light',
allowIndexing = true,
env = {},
}: {
children: React.ReactNode
nonce: string
theme?: Theme
allowIndexing?: boolean
env?: Record<string, string>
}) {
return (
Expand All @@ -193,6 +195,9 @@ function Document({
<Meta />
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
{allowIndexing ? null : (
<meta name="robots" content="noindex, nofollow" />
)}
<Links />
</head>
<body className="bg-background text-foreground">
Expand Down Expand Up @@ -240,10 +245,16 @@ function App() {
const data = useLoaderData<typeof loader>()
const nonce = useNonce()
const theme = useTheme()
const allowIndexing = data.ENV.ALLOW_INDEXING !== 'false'
useToast(data.toast)

return (
<Document nonce={nonce} theme={theme} env={data.ENV}>
<Document
nonce={nonce}
theme={theme}
allowIndexing={allowIndexing}
env={data.ENV}
>
<AppBody />
<EpicToaster closeButton position="top-center" theme={theme} />
<EpicProgress />
Expand Down
4 changes: 3 additions & 1 deletion app/utils/auth.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,9 @@ export async function signup({
email: email.toLowerCase(),
username: username.toLowerCase(),
name,
roles: { connect: { name: 'user' } },
roles: {
connect: [{ name: 'admin' }, { name: 'user' }],
},
password: {
create: {
hash: hashedPassword,
Expand Down
2 changes: 1 addition & 1 deletion app/utils/email.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export async function sendEmail({
| { html: string; text: string; react?: never }
| { react: ReactElement; html?: never; text?: never }
)) {
const from = '[email protected]'
const from = process.env.RESEND_EMAIL_ADDRESS

const email = {
from,
Expand Down
2 changes: 2 additions & 0 deletions app/utils/env.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const schema = z.object({
GITHUB_CLIENT_ID: z.string().default('MOCK_GITHUB_CLIENT_ID'),
GITHUB_CLIENT_SECRET: z.string().default('MOCK_GITHUB_CLIENT_SECRET'),
GITHUB_TOKEN: z.string().default('MOCK_GITHUB_TOKEN'),
ALLOW_INDEXING: z.enum(['true', 'false']).optional(),
})

declare global {
Expand Down Expand Up @@ -50,6 +51,7 @@ export function getEnv() {
return {
MODE: process.env.NODE_ENV,
SENTRY_DSN: process.env.SENTRY_DSN,
ALLOW_INDEXING: process.env.ALLOW_INDEXING,
}
}

Expand Down
1 change: 1 addition & 0 deletions fly.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ primary_region = "bos"
kill_signal = "SIGINT"
kill_timeout = 5
processes = [ ]
swap_size_mb = 512

[experimental]
allowed_public_ports = [ ]
Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,14 @@
"typecheck": "tsc",
"typecheck:watch": "tsc -w",
"validate": "run-p \"test -- --run\" lint typecheck test:e2e:run",
"fly:status:prod": "npm run fly:status -- --env=production",
"fly:status:staging": "npm run fly:status -- --env=staging",
"fly:status": "./scripts/fly.io/app-status.sh",
"fly:console:prisma:studio:prod": "npm run fly:console:prisma:studio -- --env=production",
"fly:console:prisma:studio:staging": "npm run fly:console:prisma:studio -- --env=staging",
"fly:console:prisma:studio": "./scripts/fly.io/app-console-prisma-studio.sh",
"fly:console:prisma:studio:proxy:prod": "npm run fly:console:prisma:studio:proxy -- --env=production",
"fly:console:prisma:studio:proxy:staging": "npm run fly:console:prisma:studio:proxy -- --env=staging",
"fly:console:prisma:studio:proxy": "./scripts/fly.io/app-console-prisma-studio-proxy.sh",
"prepare": "husky install"
},
Expand Down
44 changes: 44 additions & 0 deletions scripts/fly.io/app-console-prisma-studio-proxy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,50 @@
# run proxy to prisma studio on fly app to local port (separate terminal)
# npm run fly:app:console:prisma:studio:proxy

# Check if the script is running in production environment
if [ "$NODE_ENV" = "production" ]; then
echo "This script should not run in production."
exit 1
fi

# Default environment
ENV="development"

# Parse arguments
for ARG in "$@"
do
case $ARG in
--env=*)
ENV="${ARG#*=}"
shift
;;
esac
done

# Source environment variables
source .env

# Check if the environment variable is set
if [ -z "$FLY_APP_NAME" ]; then
echo "Error: FLY_APP_NAME environment variable is not set."
exit 1
fi

# Modify the app name for staging environment
if [ "$ENV" = "staging" ]; then
FLY_APP_NAME="${FLY_APP_NAME}-staging"
elif [ "$ENV" != "production" ]; then
echo "Unknown environment: $ENV"
exit 1
fi

# Run the Fly.io proxy command
fly proxy 5556:5555 --app $FLY_APP_NAME

# FYI closing the proxy will display a warning:
# "Error: ssh shell: session forcibly closed; the remote process may still be running"
# don't worry :D
# https://community.fly.io/t/does-fly-know-when-my-app-decides-to-gracefully-shutdown/19132
# the comments from this post seem to suggest the healthcheck fail will close the connection
# https://fly.io/docs/reference/configuration/#services-tcp_checks
# here is the doc to confirm :D
36 changes: 36 additions & 0 deletions scripts/fly.io/app-console-prisma-studio.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,42 @@
# run proxy to prisma studio on fly app to local port (separate terminal)
# npm run fly:app:console:prisma:studio:proxy

# Check if the script is running in production environment
if [ "$NODE_ENV" = "production" ]; then
echo "This script should not run in production."
exit 1
fi

# Default environment
ENV="development"

# Parse arguments
for ARG in "$@"
do
case $ARG in
--env=*)
ENV="${ARG#*=}"
shift
;;
esac
done

# Source environment variables
source .env

# Check if the environment variable is set
if [ -z "$FLY_APP_NAME" ]; then
echo "Error: FLY_APP_NAME environment variable is not set."
exit 1
fi

# Modify the app name for staging environment
if [ "$ENV" = "staging" ]; then
FLY_APP_NAME="${FLY_APP_NAME}-staging"
elif [ "$ENV" != "production" ]; then
echo "Unknown environment: $ENV"
exit 1
fi

# Run the Fly.io ssh console command
fly ssh console -C "npm run prisma:studio" --app $FLY_APP_NAME
36 changes: 36 additions & 0 deletions scripts/fly.io/app-status.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,42 @@
# to give permission to execute the file locally run:
# chmod +x scripts/fly.io/app-status.sh

# Check if the script is running in production environment
if [ "$NODE_ENV" = "production" ]; then
echo "This script should not run in production."
exit 1
fi

# Default environment
ENV="development"

# Parse arguments
for ARG in "$@"
do
case $ARG in
--env=*)
ENV="${ARG#*=}"
shift
;;
esac
done

# Source environment variables
source .env

# Check if the environment variable is set
if [ -z "$FLY_APP_NAME" ]; then
echo "Error: FLY_APP_NAME environment variable is not set."
exit 1
fi

# Modify the app name for staging environment
if [ "$ENV" = "staging" ]; then
FLY_APP_NAME="${FLY_APP_NAME}-staging"
elif [ "$ENV" != "production" ]; then
echo "Unknown environment: $ENV"
exit 1
fi

# Run the Fly.io status command
fly status --app $FLY_APP_NAME
8 changes: 8 additions & 0 deletions server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ installGlobals()
const MODE = process.env.NODE_ENV ?? 'development'
const IS_PROD = MODE === 'production'
const IS_DEV = MODE === 'development'
const ALLOW_INDEXING = process.env.ALLOW_INDEXING !== 'false'

const createRequestHandler = IS_PROD
? Sentry.wrapExpressCreateRequestHandler(_createRequestHandler)
Expand Down Expand Up @@ -208,6 +209,13 @@ async function getBuild() {
return build as unknown as ServerBuild
}

if (!ALLOW_INDEXING) {
app.use((_, res, next) => {
res.set('X-Robots-Tag', 'noindex, nofollow')
next()
})
}

app.all(
'*',
createRequestHandler({
Expand Down
Loading

0 comments on commit e8c1638

Please sign in to comment.