Skip to content

Commit

Permalink
Update CI workflows (#146)
Browse files Browse the repository at this point in the history
Co-authored-by: Ilya Egorov <[email protected]>
rahulyadav-57 and Ilya Egorov authored Nov 2, 2024

Verified

This commit was signed with the committer’s verified signature.
snyk-bot Snyk bot
1 parent 0ef841f commit fcad599
Showing 27 changed files with 795 additions and 150 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ module.exports = {
ecmaVersion: 2020,
project: "./tsconfig.json",
},
ignorePatterns: ["*.cjs", "!.lintstagedrc.js", ".lintstagedrc.js"],
ignorePatterns: ["*.cjs", "!.lintstagedrc.js", ".lintstagedrc.js", "next.config.js"],
plugins: ["@typescript-eslint"],
root: true,
env: {
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/helm/ @Mobyman
/.github/ @Mobyman
126 changes: 126 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
name: Deploy to Kubernetes

on:
push:
branches:
- main
- canary
- staging

jobs:
build:
runs-on: ubuntu-latest
env:
APP_ENV: ${{ github.ref == 'refs/heads/main' && 'production' || github.ref == 'refs/heads/canary' && 'canary' || github.ref == 'refs/heads/staging' && 'staging' || 'unknown' }}
APP_DOMAIN: ${{ github.ref == 'refs/heads/staging' && vars.APP_DOMAIN_STAGING || vars.APP_DOMAIN }}

permissions:
packages: write
contents: read

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set sha-short
run: echo "GITHUB_SHA_SHORT=$(echo $GITHUB_SHA | cut -c 1-7)" >> $GITHUB_ENV

- id: lower-repo
name: Repository to lowercase
run: |
echo "repository=${GITHUB_REPOSITORY@L}" >> $GITHUB_OUTPUT
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ steps.lower-repo.outputs.repository }}
github-token: ${{ secrets.GITHUB_TOKEN }}
tags: |
type=sha
type=sha,format=long
type=ref,event=branch
- name: Build and push Docker image ${{ steps.lower-repo.outputs.repository }}:${{ env.APP_ENV }}
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ghcr.io/${{ steps.lower-repo.outputs.repository }}:${{ env.GITHUB_SHA_SHORT }},ghcr.io/${{ steps.lower-repo.outputs.repository }}:${{ env.APP_ENV }}
build-args: |
sha=${{ github.sha }}
sha_short=${{ env.GITHUB_SHA_SHORT }}
app_env=${{ vars.APP_ENV }}
- name: Apply AWS k8s config
run: aws eks update-kubeconfig --name ${{ vars.AWS_CLUSTER }} --region ${{ vars.AWS_REGION }}

- name: Create namespace
run: |
kubectl create ns ${{ vars.APP_NAME }}-${{ env.APP_ENV }} || echo "Namespace $EKS_NAMESPACE already exists"
- name: Deploy ${{ vars.APP_NAME }} to Kubernetes
run: |
helm upgrade --install ${{ vars.APP_NAME }} ./helm/app \
--namespace ${{ vars.APP_NAME }}-${{ env.APP_ENV }} \
--values ./helm/app/values.yaml \
--values ./helm/app/values-${{ env.APP_ENV }}.yaml \
--set imageRepo="ghcr.io/${{ steps.lower-repo.outputs.repository }}" \
--set imageTag="${{ env.GITHUB_SHA_SHORT }}" \
--set host=${{ env.APP_DOMAIN }} \
--set appName=${{ vars.APP_NAME }} \
--set ghcrSecret=${{ secrets.GHCR_SECRET }} \
--set secrets.publicProxyKey=${{ secrets.NEXT_PUBLIC_MIXPANEL_TOKEN }} \
--set secrets.publicMixPanelToken=${{ secrets.NEXT_PUBLIC_ANALYTICS_ENABLED }} \
--set secrets.publicProxyKey=${{ secrets.NEXT_PUBLIC_ANALYTICS_ENABLED }}
- name: Verify deployment
run: |
kubectl -n ${{ vars.APP_NAME }}-${{ env.APP_ENV }} rollout status deployment/${{ vars.APP_NAME }}-${{ env.APP_ENV }}
- name: Telegram Notify
uses: appleboy/[email protected]
if: success() && contains('${{ vars.ENABLE_DEPLOY_BOT }}', 1)
with:
to: ${{ secrets.TELEGRAM_DEPLOY_CHAT_ID }}
token: ${{ secrets.TELEGRAM_DEPLOY_TOKEN }}
format: markdown
message: |
🚂 The application from repository [${{ steps.lower-repo.outputs.repository }}](https://github.com/${{ steps.lower-repo.outputs.repository }}) has been successfully deployed by [${{ github.actor }}](https://github.com/users/${{ github.actor }}) on ${{ env.APP_ENV }}.
🏗️ [GitHub Actions Build](https://github.com/${{ steps.lower-repo.outputs.repository }}/actions/runs/${{ github.run_id }})
🐳 [Image](https://ghcr.io/${{ steps.lower-repo.outputs.repository }}:${{ env.GITHUB_SHA_SHORT }}
🔗 [Link](https://${{ env.APP_DOMAIN }})
- name: Telegram Notify
uses: appleboy/[email protected]
if: failure()
with:
to: ${{ secrets.TELEGRAM_DEPLOY_CHAT_ID }}
token: ${{ secrets.TELEGRAM_DEPLOY_TOKEN }}
format: markdown
message: |
🚨Deploy of the application from repository [${{ steps.lower-repo.outputs.repository }}](https://github.com/${{ steps.lower-repo.outputs.repository }}) on ${{ env.APP_ENV }} has been failed.
🏗️ [GitHub Actions Build](https://github.com/${{ steps.lower-repo.outputs.repository }}/actions/runs/${{ github.run_id }})
🐳 [Image](https://ghcr.io/${{ steps.lower-repo.outputs.repository }}:${{ env.GITHUB_SHA_SHORT }}
🔗 [Link](https://${{ env.APP_DOMAIN }})
72 changes: 0 additions & 72 deletions .github/workflows/main.yml

This file was deleted.

63 changes: 63 additions & 0 deletions .github/workflows/rollback.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Rollback Kubernetes Deployment

on:
workflow_dispatch:
inputs:
app_env:
description: "Select the environment"
required: true
default: production
type: choice
options:
- production
- staging
revision:
description: "Choose the Helm revision to rollback to"
required: false

jobs:
rollback:
runs-on: ubuntu-latest

steps:
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}

- name: Apply AWS k8s config
run: aws eks update-kubeconfig --name ${{ vars.AWS_CLUSTER }} --region ${{ vars.AWS_REGION }}

- name: Fetch Helm history
run: |
helm history ${{ vars.APP_NAME }} --namespace ${{ vars.APP_NAME }}-${{ github.event.inputs.app_env }}
- name: Get revision number
if: github.event.inputs.revision == ''
run: |
echo "No revision provided. Exiting."
exit 1
- name: Perform Helm rollback
run: |
helm rollback ${{ vars.APP_NAME }} ${{ github.event.inputs.revision }} --namespace ${{ vars.APP_NAME }}-${{ github.event.inputs.app_env }} ${{ github.event.inputs.revision }}
- name: Verify rollback
run: |
kubectl -n ${{ vars.APP_NAME }}-${{ github.event.inputs.app_env }} rollout status deployment/${{ vars.APP_NAME }}-${{ github.event.inputs.app_env }}
- name: Show Helm history
run: |
helm history ${{ vars.APP_NAME }} --namespace ${{ vars.APP_NAME }}-${{ github.event.inputs.app_env }}
- name: Telegram Notify
uses: appleboy/[email protected]
with:
to: ${{ secrets.TELEGRAM_DEPLOY_CHAT_ID }}
token: ${{ secrets.TELEGRAM_DEPLOY_TOKEN }}
format: markdown
message: |
🔄 The deployment {{ app_env }} has been rolled back by [${{ github.actor }}](https://github.com/${{ github.actor }}) to revision ${{ github.event.inputs.revision }}.
🏗️ [GitHub Actions Run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions .idea/material_theme_project_new.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/web-ide.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/helm
10 changes: 10 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM node:21-alpine AS base

WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

EXPOSE 3000
CMD ["npm", "start"]
4 changes: 4 additions & 0 deletions helm/app/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
apiVersion: v2
name: Node.js Chart
description: A Helm chart for deploying my Node.js application
version: 0.1.2
56 changes: 56 additions & 0 deletions helm/app/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.appName }}-{{ .Values.deployEnv}}
namespace: {{ .Release.Namespace }}
labels:
app: {{ .Values.appName }}-{{ .Values.deployEnv }}
release: prometheus-stack
spec:
replicas: {{ .Values.defaultReplicaCount }}
strategy:
type: RollingUpdate
selector:
matchLabels:
app: {{ .Values.appName }}-{{ .Values.deployEnv}}
template:
metadata:
labels:
app: {{ .Values.appName }}-{{ .Values.deployEnv}}
release: prometheus-stack
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: {{ .Values.appName }}-{{ .Values.deployEnv }}
matchLabelKeys:
- pod-template-hash
containers:
- name: {{ .Values.appName }}-{{ .Values.deployEnv}}
image: "{{ .Values.imageRepo }}:{{ .Values.imageTag }}"
env:
- name: APP_ENV
value: {{ .Values.deployEnv }}
- name: APP_VERSION
value: {{ .Values.appVersion | quote }}
- name: NEXT_PUBLIC_PROXY_KEY
value: {{ .Values.secrets.publicProxyKey | quote }}
- name: NEXT_PUBLIC_MIXPANEL_TOKEN
value: {{ .Values.secrets.publicMixPanelToken | quote }}
- name: NEXT_PUBLIC_ANALYTICS_ENABLED
value: {{ .Values.secrets.publicProxyKey | quote }}
ports:
- containerPort: {{ .Values.containerPort }}
resources:
limits:
cpu: {{ .Values.cpuLimit }}
memory: {{ .Values.memoryLimit }}
requests:
cpu: {{ .Values.cpuRequest }}
memory: {{ .Values.memoryRequest }}
imagePullPolicy: Always
imagePullSecrets:
- name: dockerconfigjson-github-com
10 changes: 10 additions & 0 deletions helm/app/templates/ghcr-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
kind: Secret
type: kubernetes.io/dockerconfigjson
apiVersion: v1
metadata:
name: dockerconfigjson-github-com
namespace: {{ .Release.Namespace }}
labels:
app: {{ .Values.appName }}-{{ .Values.deployEnv}}
data:
.dockerconfigjson: {{ .Values.ghcrSecret }}
25 changes: 25 additions & 0 deletions helm/app/templates/hpa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ .Values.appName }}-{{ .Values.deployEnv}}
namespace: {{ .Release.Namespace }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ .Values.appName }}-{{ .Values.deployEnv}}
minReplicas: {{ .Values.minReplicas }}
maxReplicas: {{ .Values.maxReplicas }}
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
46 changes: 46 additions & 0 deletions helm/app/templates/ingress.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{{ if .Values.publicService }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Values.appName }}-{{ .Values.deployEnv}}
namespace: {{ .Release.Namespace }}
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/ssl-redirect: "{{ .Values.sslRedirect }}"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "10s"
nginx.ingress.kubernetes.io/proxy-read-timeout: "15s"
nginx.ingress.kubernetes.io/proxy-send-timeout: "15s"
nginx.ingress.kubernetes.io/from-to-www-redirect: "true"
nginx.ingress.kubernetes.io/proxy-next-upstream: "error timeout http_502 http_503 http_504"
nginx.ingress.kubernetes.io/proxy-next-upstream-tries: "3"
cert-manager.io/cluster-issuer: {{ .Values.tlsIssuer }}
{{- if eq .Values.deployEnv "canary" }}
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-cookie: {{ .Values.canaryCookie | quote }}
nginx.ingress.kubernetes.io/canary-weight: {{ .Values.canaryWeight | quote }}
{{- end }}
nginx.ingress.kubernetes.io/server-snippet: |
location ~ ^/(metrics|ready|health)$ {
return 403;
}
labels:
release: prometheus-stack
app: {{ .Values.appName }}-{{ .Values.deployEnv}}
spec:
tls:
- hosts:
- {{ .Values.host }}
secretName: {{ .Values.host }}
rules:
- host: {{ .Values.host }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Values.appName }}-{{ .Values.deployEnv }}
port:
name: http
{{- end }}
26 changes: 26 additions & 0 deletions helm/app/templates/ns-resource-quota.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
apiVersion: v1
kind: ResourceQuota
metadata:
name: resource-quota
namespace: {{ .Release.Namespace }}
spec:
hard:
{{- if eq .Values.deployEnv "staging" }}
pods: "2"
requests.memory: "256Mi"
limits.cpu: "1"
limits.memory: "16Gi"
persistentvolumeclaims: "0"
{{- end }}
{{- if eq .Values.deployEnv "canary" }}
pods: "10"
limits.cpu: "4"
limits.memory: "8Gi"
persistentvolumeclaims: "0"
{{- end }}
{{- if eq .Values.deployEnv "production" }}
pods: "100"
limits.cpu: "8"
limits.memory: "16Gi"
persistentvolumeclaims: "0"
{{- end }}
19 changes: 19 additions & 0 deletions helm/app/templates/service-monitor.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ .Values.appName }}-{{ .Values.deployEnv}}
namespace: {{ .Release.Namespace }}
labels:
release: prometheus-stack
spec:
selector:
matchLabels:
app: {{ .Values.appName }}-{{ .Values.deployEnv}}
endpoints:
- port: http
interval: 30s
path: /metrics
- port: https
interval: 30s
path: /metrics

17 changes: 17 additions & 0 deletions helm/app/templates/service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.appName }}-{{ .Values.deployEnv}}
namespace: {{ .Release.Namespace }}
labels:
app: {{ .Values.appName }}-{{ .Values.deployEnv }}
release: prometheus-stack
spec:
type: ClusterIP
selector:
app: {{ .Values.appName }}-{{ .Values.deployEnv}}
ports:
- name: http
protocol: TCP
port: 80
targetPort: {{ .Values.containerPort }}
10 changes: 10 additions & 0 deletions helm/app/values-canary.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
deployEnv: canary
replicaCount: 2
imageTag: "canary"
canaryWeight: 10

minReplicas: 1
maxReplicas: 1

memoryLimit: 120Mi
memoryRequest: 100Mi
9 changes: 9 additions & 0 deletions helm/app/values-production.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
deployEnv: production
defaultReplicaCount: 2
imageTag: "production"

minReplicas: 2
maxReplicas: 4

memoryLimit: 120Mi
memoryRequest: 100Mi
9 changes: 9 additions & 0 deletions helm/app/values-staging.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
deployEnv: staging
replicaCount: 1
imageTag: "staging"

minReplicas: 1
maxReplicas: 1

memoryLimit: 120Mi
memoryRequest: 100Mi
42 changes: 42 additions & 0 deletions helm/app/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# app
appVersion: "0.1"

# limits & requests
cpuLimit: "500m"
memoryLimit: "128Mi"
cpuRequest: "500m"
memoryRequest: "64Mi"

# replicas
minReplicas: 2
maxReplicas: 40

# docker
containerPort: 3000
nodePort: 80

# from github deploy
imageRepo: ""
imageTag: ""
host: ""
appName: ""
ghcrSecret: ""

tlsCert: ""
tlsKey: ""

canaryCookie: "canary_tPIzU7rz5ecBWK2gFOs72o5s2qr0kz"

# do not change
tlsIssuer: "letsencrypt"
certIssuingMode: false

# http
publicService: true
sslRedirect: false


secrets:
publicProxyKey: ""
publicMixPanelToken: ""
secrets.publicProxyKey: ""
3 changes: 3 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -9,6 +9,9 @@ const webpack = require("webpack");

const nextConfig = withTM({
reactStrictMode: true,
images: {
unoptimized: true,
},
webpack: (config, options) => {
config.resolve.fallback = { fs: false };
config.resolve.alias = {
342 changes: 270 additions & 72 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -10,8 +10,8 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
"format:check": "prettier --check --ignore-path .gitignore .",
"format:fix": "prettier --write --ignore-path .gitignore .",
"format:check": "prettier --check --ignore-path .prettierignore --ignore-path .gitignore .",
"format:fix": "prettier --write --ignore-path .prettierignore --ignore-path .gitignore .",
"prepare": "husky",
"commit": "git-cz"
},
@@ -40,7 +40,7 @@
"@xterm/addon-search": "^0.14.0-beta.1",
"@zip.js/zip.js": "^2.7.6",
"antd": "^5.2.3",
"axios": "^1.6.7",
"axios": "^1.7.4",
"bn.js": "^5.2.1",
"change-case": "^5.4.4",
"clsx": "^1.2.1",
@@ -65,11 +65,11 @@
"react-markdown": "^9.0.1",
"react-split": "^2.0.14",
"react-syntax-highlighter": "^15.5.0",
"react-use": "^17.4.0",
"react-use": "^17.5.1",
"recoil": "^0.7.7",
"recoil-persist": "^4.2.0",
"reconnecting-websocket": "^4.4.0",
"rollup": "^4.20.0",
"rollup": "^4.22.4",
"sass": "^1.58.3",
"ts-browser-eval": "^0.0.1",
"typescript": "4.9.5",

0 comments on commit fcad599

Please sign in to comment.