Skip to content

Commit 08634e3

Browse files
committed
initial commit 🐘
0 parents  commit 08634e3

26 files changed

+8585
-0
lines changed

.dockerignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Environment files should not be copied and pushed to a container registry!
2+
.env*
3+
4+
# Host node_modules, and .next folder should not be copied into the container.
5+
# Issues related to macOS vs Linux binaries for native addons etc. could occur
6+
node_modules
7+
.next

.env.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# A Neon postgresql connection string. Visit https://console.neon.tech/ to obtain yours
2+
DATABASE_URL = ""
3+
4+
# The same Neon postgresql connection string without the "pgbouncer=true" query parameter
5+
DIRECT_DATABASE_URL = ""

.eslintrc.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "next/core-web-vitals"
3+
}
4+
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
name: Build Container Image and Preview Environment
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
branches:
7+
- '*'
8+
9+
jobs:
10+
build-container:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout source code
15+
uses: actions/checkout@v3
16+
17+
- name: Login to Docker Hub
18+
uses: docker/login-action@v3
19+
with:
20+
username: ${{ secrets.DOCKERHUB_USERNAME }}
21+
password: ${{ secrets.DOCKERHUB_TOKEN }}
22+
23+
- name: Build and Push
24+
uses: docker/build-push-action@v5
25+
with:
26+
push: true
27+
context: elements-application/
28+
file: elements-application/Containerfile
29+
tags: ${{ secrets.DOCKERHUB_USERNAME }}/neon-kube-previews:${{ github.event.pull_request.head.sha }}
30+
31+
create-preview-environment:
32+
runs-on: ubuntu-latest
33+
34+
# Wait for the docker build and push to complete prior to running this job.
35+
# Technically this could run in parallel, but it doesn't make sense to add
36+
# a preview URL if the build step fails
37+
needs: build-container
38+
39+
# This permission is required to comment with the preview URL on the PR
40+
permissions:
41+
pull-requests: write
42+
43+
steps:
44+
# Create a new branch on Neon using the PR number in the branch name. This
45+
# operation is idempotent, so it's effectively a no-op if more commits are
46+
# push to the original PR
47+
- name: Create Neon Branch and Compute for PR
48+
id: create-branch
49+
uses: neondatabase/create-branch-action@v4
50+
with:
51+
api_key: ${{ secrets.NEON_API_KEY }}
52+
project_id: ${{ secrets.NEON_PROJECT_ID }}
53+
branch_name: pr-${{ github.event.number }}
54+
parent: main
55+
56+
- name: Install the Argo CD CLI
57+
run: |
58+
curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/download/v2.8.4/argocd-linux-amd64
59+
sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd
60+
rm argocd-linux-amd64
61+
62+
- name: Login to Argo CD
63+
run: argocd login ${{ secrets.ARGOCD_HOSTNAME }} --username ${{ secrets.ARGOCD_USERNAME }} --password ${{ secrets.ARGOCD_PASSWORD }}
64+
65+
- name: Update the Preview Environment with the Neon Branch URL
66+
run: argocd app set nkp-pr-${{github.event.number}} --parameter database.url=${{ steps.create-branch.outputs.db_url }}
67+
68+
- name: Comment on Pull Request
69+
uses: thollander/actions-comment-pull-request@v2
70+
with:
71+
message: |
72+
:rocket: Preview URL: https://pr-${{github.event.number}}.${{ secrets.PREVIEW_SUBDOMAIN }}
73+
:octopus: Argo CD URL: https://${{ secrets.ARGOCD_HOSTNAME }}/applications/argocd/nkp-pr-${{github.event.number}}
74+
# Comment tag allows the action to update an existing comment, if one
75+
# exists. This prevents spamming the PR with identical comments
76+
comment_tag: preview-url

.gitignore

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# next.js
12+
/.next/
13+
/out/
14+
15+
# production
16+
/build
17+
18+
# misc
19+
.DS_Store
20+
*.pem
21+
22+
# debug
23+
npm-debug.log*
24+
yarn-debug.log*
25+
yarn-error.log*
26+
.pnpm-debug.log*
27+
28+
# local env files
29+
.env
30+
31+
# vercel
32+
.vercel
33+
34+
# typescript
35+
*.tsbuildinfo
36+
next-env.d.ts

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v18.18.0

Dockerfile

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# This Dockerfile/Containerfile is based on the official Next.js sample:
2+
# https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile
3+
FROM node:18 AS base
4+
5+
# Install dependencies only when needed
6+
FROM base AS deps
7+
8+
WORKDIR /app
9+
10+
COPY package*.json ./
11+
RUN \
12+
if [ -f package-lock.json ]; then npm ci; \
13+
else echo "A package-lock.json was not found. Run 'npm i --package-lock-only' to generate it." && exit 1; \
14+
fi
15+
16+
# Rebuild the source code and schemas only when needed
17+
FROM base AS builder
18+
WORKDIR /app
19+
COPY --from=deps /app/node_modules ./node_modules
20+
COPY . .
21+
22+
ENV NEXT_TELEMETRY_DISABLED 1
23+
RUN npx prisma generate
24+
RUN npm run build
25+
26+
# Production image, copy all the files and run next
27+
FROM base AS runner
28+
WORKDIR /app
29+
30+
ENV NODE_ENV production
31+
ENV NEXT_TELEMETRY_DISABLED 1
32+
33+
RUN addgroup --system --gid 1001 nodejs
34+
RUN adduser --system --uid 1001 nextjs
35+
36+
COPY --from=builder /app/public ./public
37+
38+
# Set the correct permission for prerender cache
39+
RUN mkdir .next
40+
RUN chown nextjs:nodejs .next
41+
42+
# Automatically leverage output traces to reduce image size
43+
# https://nextjs.org/docs/advanced-features/output-file-tracing
44+
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
45+
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
46+
47+
USER nextjs
48+
49+
EXPOSE 3000
50+
51+
# These can be overwritten using "docker run -e", or in a deployment.yaml
52+
# when being run on kubernetes
53+
ENV PORT 3000
54+
ENV HOSTNAME "0.0.0.0"
55+
56+
CMD ["node", "server.js"]

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Neon Branching for Kubernetes-based Preview Environments
2+
3+
## How it Works
4+
5+
This repository contains source code for a sample Next.js application. This
6+
application can be developed locally, and then built into a container image
7+
using GitHub Actions workflows.
8+
9+
Each pull request opened against the repository will trigger a container image
10+
build, and will also result in a preview environment being created on a
11+
development Kubernetes cluster. The preview environment creation is handled by
12+
an [Argo CD ApplicationSet](https://argo-cd.readthedocs.io/en/stable/user-guide/application-set/)
13+
and the manifests defined in [this manifest repository](https://github.com/evanshortiss/neon-kube-previews-manifests).
14+
Once the build associated with a pull request is complete, it will comment on
15+
the pull request with a link to the preview environment.
16+
17+
## Requirements
18+
19+
To run the GitHub Actions workflow, add the following secrets to the repository
20+
using the **Settings > Secrets and variables > Actions** screen:
21+
22+
* `DOCKERHUB_TOKEN` - A token with write access to a repository on Docker Hub.
23+
* `DOCKERHUB_USERNAME` - The username that owns the `neon-kube-previews` repository that the container image will be written to.
24+
* `NEON_API_KEY` - Found in the [Developer Settings](https://console.neon.tech/app/settings/api-keys) screen on the Neon console.
25+
* `NEON_PROJECT_ID` - Found in *Settings > General* on the Neon project dashboard.
26+
* `ARGOCD_HOSTNAME` - Strictly the hostname, e.g `argocd.foo.bar` without `https`.
27+
* `ARGOCD_USERNAME` - A valid Argo CD username. You can can use `admin` if you're prototyping.
28+
* `ARGOCD_PASSWORD` - The password associated with the given `ARGOCD_USERNAME`.
29+
* `PREVIEW_SUBDOMAIN` - The subdomain that hosts preview environments, e.g `neon.ngrok.app`. This will be used to form a full preview environment URL, i.e `https://pr-1.${PREVIEW_SUBDOMAIN}`
30+
31+
If you're unfamiliar with how to add secrets to a GitHub repository, you can read more about in [GitHub's documentation](https://docs.github.com/en/actions/security-guides/encrypted-secrets).

app/favicon.ico

38.6 KB
Binary file not shown.

app/globals.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@tailwind base;
2+
@tailwind components;
3+
@tailwind utilities;

app/layout.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import './globals.css';
2+
import { Inter } from 'next/font/google';
3+
4+
const inter = Inter({ subsets: ['latin'] });
5+
6+
export const metadata = {
7+
title: 'Next.js + Prisma + Neon',
8+
description:
9+
'Next.js example app with Prisma as the ORM and Neon as the Postgres database',
10+
};
11+
12+
type RouteLayoutProps = {
13+
children: React.ReactNode;
14+
};
15+
16+
export default function RootLayout({ children }: RouteLayoutProps) {
17+
return (
18+
<html lang="en" className={inter.className}>
19+
<body>{children}</body>
20+
</html>
21+
);
22+
}

app/page.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { prisma } from '~/lib/prisma';
2+
3+
export const dynamic = 'force-dynamic';
4+
5+
async function getData () {
6+
const elements = await prisma.element.findMany();
7+
8+
return elements
9+
}
10+
11+
export default async function Home () {
12+
const elements = await getData()
13+
const HOSTNAME = process.env.HOSTNAME
14+
return (
15+
<main className="min-h-screen flex items-center justify-center bg-[#1A1A1A]">
16+
<ul className="grid grid-cols-2 md:grid-cols-5 gap-5">
17+
{elements.map((element:any) => (
18+
<li
19+
key={element.id}
20+
className="relative flex flex-col text-center p-5 rounded-md bg-[#00E699] transition-colors hover:bg-[#00e5BF] text-[black]"
21+
>
22+
<p className="absolute top-2 left-2 text-sm">
23+
{element.atomicNumber}
24+
</p>
25+
<h2 className="text-2xl font-medium">{element.symbol}</h2>
26+
<p className="text-base">{element.elementName}</p>
27+
</li>
28+
))}
29+
<p>{HOSTNAME}</p>
30+
</ul>
31+
</main>
32+
);
33+
}

lib/prisma.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { PrismaClient } from '@prisma/client';
2+
import { get } from 'env-var'
3+
import { PHASE_PRODUCTION_BUILD } from 'next/constants';
4+
const globalForPrisma = global as unknown as { prisma: PrismaClient };
5+
6+
const NODE_ENV = get('NODE_ENV').default('development').asEnum(['development', 'production'])
7+
const NEXT_PHASE = get('NEXT_PHASE').asString()
8+
9+
const isProductionEnv = NODE_ENV === 'production'
10+
const isBuildingProduction = NEXT_PHASE === PHASE_PRODUCTION_BUILD
11+
12+
// This will throw an error if the DATABASE_URL environment variable is missing
13+
// at runtime, but will not throw when compiling a production build using next
14+
const DATABASE_URL = get('DATABASE_URL').required(isProductionEnv && !isBuildingProduction).asUrlString()
15+
16+
export const prisma =
17+
globalForPrisma.prisma ||
18+
new PrismaClient({
19+
datasources: {
20+
db: {
21+
// when using a pooled database connection with prisma, you need to append`?pgbouncer=true` to the connection string.
22+
// The reason this is done here rather than in the .env file is because the Neon Vercel integration doesn't include it.
23+
url: `${DATABASE_URL}?pgbouncer=true&connect_timeout=10&pool_timeout=10`,
24+
},
25+
},
26+
});
27+
28+
if (isProductionEnv) globalForPrisma.prisma = prisma;

next.config.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/** @type {import('next').NextConfig} */
2+
const nextConfig = {
3+
// This is necessary to support deployment as a via container image
4+
output: 'standalone'
5+
}
6+
7+
module.exports = nextConfig

0 commit comments

Comments
 (0)