chore: inject enviroment at the ./front deployment phase (#2174) (#2179)

* chore: inject enviroment at the deployment phase (#2174)

* Dockerfile CMD env.sh
* env.sh generates env-config.js file
* index.html imports env-config.js
* front/src/config/index.ts imports REACT_APP_SERVER_BASE_URL

* Upgrade Dockerfiles

* Add compute pg_database_url for render

* fix tests

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Ruslan
2023-10-22 17:36:36 +07:00
committed by GitHub
parent 1954ed5e3a
commit a5fe256d7e
15 changed files with 52 additions and 15 deletions

View File

@ -66,7 +66,8 @@
}, },
"scripts": { "scripts": {
"start": "PORT=3001 craco start --max-warnings=0", "start": "PORT=3001 craco start --max-warnings=0",
"build": "craco build", "build:inject-runtime-env": "./scripts/inject-runtime-env.sh",
"build": "craco build && yarn build:inject-runtime-env",
"test": "craco test", "test": "craco test",
"coverage": "craco test --coverage .", "coverage": "craco test --coverage .",
"lint": "eslint src --max-warnings=0", "lint": "eslint src --max-warnings=0",

View File

@ -0,0 +1,3 @@
window._env_ = {
// This file should stay empty. It will be overwritten by the build process.
}

View File

@ -36,6 +36,7 @@
/> />
<title>Twenty</title> <title>Twenty</title>
<script src="%PUBLIC_URL%/env-config.js"></script>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -0,0 +1,11 @@
echo "Generating env-config.js file from runtime environment variables..."
BASE_FILENAME="build/env-config.js"
mkdir -p build
rm -rf "./$BASE_FILENAME"
{
echo "window._env_ = {"
echo " REACT_APP_SERVER_BASE_URL: \"$REACT_APP_SERVER_BASE_URL\","
echo "}"
} > "./$BASE_FILENAME"

10
front/src/config/index.ts Normal file
View File

@ -0,0 +1,10 @@
declare global {
interface Window {
_env_?: Record<string, string>;
}
}
export const REACT_APP_SERVER_BASE_URL =
window._env_?.REACT_APP_SERVER_BASE_URL ||
process.env.REACT_APP_SERVER_BASE_URL ||
'http://localhost:3000';

View File

@ -6,6 +6,7 @@ import { useRecoilState } from 'recoil';
import { tokenPairState } from '@/auth/states/tokenPairState'; import { tokenPairState } from '@/auth/states/tokenPairState';
import { isDebugModeState } from '@/client-config/states/isDebugModeState'; import { isDebugModeState } from '@/client-config/states/isDebugModeState';
import { AppPath } from '@/types/AppPath'; import { AppPath } from '@/types/AppPath';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { ActivityTarget } from '~/generated/graphql'; import { ActivityTarget } from '~/generated/graphql';
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
import { useUpdateEffect } from '~/hooks/useUpdateEffect'; import { useUpdateEffect } from '~/hooks/useUpdateEffect';
@ -23,7 +24,7 @@ export const useApolloFactory = () => {
const apolloClient = useMemo(() => { const apolloClient = useMemo(() => {
apolloRef.current = new ApolloFactory({ apolloRef.current = new ApolloFactory({
uri: `${process.env.REACT_APP_SERVER_BASE_URL}/graphql`, uri: `${REACT_APP_SERVER_BASE_URL}/graphql`,
cache: new InMemoryCache({ cache: new InMemoryCache({
typePolicies: { typePolicies: {
Activity: { Activity: {

View File

@ -6,6 +6,7 @@ import {
useRecoilState, useRecoilState,
} from 'recoil'; } from 'recoil';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { import {
useChallengeMutation, useChallengeMutation,
useCheckUserExistsLazyQuery, useCheckUserExistsLazyQuery,
@ -136,8 +137,7 @@ export const useAuth = () => {
const handleGoogleLogin = useCallback((workspaceInviteHash?: string) => { const handleGoogleLogin = useCallback((workspaceInviteHash?: string) => {
const authServerUrl = const authServerUrl =
process.env.REACT_APP_SERVER_AUTH_URL ?? REACT_APP_SERVER_BASE_URL ?? REACT_APP_SERVER_BASE_URL + '/auth';
process.env.REACT_APP_SERVER_BASE_URL + '/auth';
window.location.href = window.location.href =
`${authServerUrl}/google/${ `${authServerUrl}/google/${
workspaceInviteHash ? '?inviteHash=' + workspaceInviteHash : '' workspaceInviteHash ? '?inviteHash=' + workspaceInviteHash : ''

View File

@ -4,6 +4,7 @@ import { ApolloClient, InMemoryCache } from '@apollo/client';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { tokenPairState } from '@/auth/states/tokenPairState'; import { tokenPairState } from '@/auth/states/tokenPairState';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { ApolloMetadataClientContext } from '../context/ApolloClientMetadataContext'; import { ApolloMetadataClientContext } from '../context/ApolloClientMetadataContext';
@ -17,7 +18,7 @@ export const ApolloMetadataClientProvider = ({
const apolloMetadataClient = useMemo(() => { const apolloMetadataClient = useMemo(() => {
if (tokenPair?.accessToken.token) { if (tokenPair?.accessToken.token) {
return new ApolloClient({ return new ApolloClient({
uri: `${process.env.REACT_APP_SERVER_BASE_URL}/metadata`, uri: `${REACT_APP_SERVER_BASE_URL}/metadata`,
cache: new InMemoryCache(), cache: new InMemoryCache(),
headers: { headers: {
Authorization: `Bearer ${tokenPair.accessToken.token}`, Authorization: `Bearer ${tokenPair.accessToken.token}`,

View File

@ -1,3 +1,5 @@
import { REACT_APP_SERVER_BASE_URL } from '~/config';
export const getImageAbsoluteURIOrBase64 = (imageUrl?: string | null) => { export const getImageAbsoluteURIOrBase64 = (imageUrl?: string | null) => {
if (!imageUrl) { if (!imageUrl) {
return null; return null;
@ -12,8 +14,7 @@ export const getImageAbsoluteURIOrBase64 = (imageUrl?: string | null) => {
} }
const serverFilesUrl = const serverFilesUrl =
process.env.REACT_APP_SERVER_FILES_URL ?? REACT_APP_SERVER_BASE_URL ?? REACT_APP_SERVER_BASE_URL + '/files';
process.env.REACT_APP_SERVER_BASE_URL + '/files';
return `${serverFilesUrl}/${imageUrl}`; return `${serverFilesUrl}/${imageUrl}`;
}; };

View File

@ -1,6 +1,5 @@
FROM node:18.16.0-alpine as build FROM node:18.16.0-alpine as build
ARG REACT_APP_SERVER_BASE_URL ARG REACT_APP_SERVER_BASE_URL
ARG REACT_APP_SERVER_AUTH_URL ARG REACT_APP_SERVER_AUTH_URL
ARG REACT_APP_SERVER_FILES_URL ARG REACT_APP_SERVER_FILES_URL
@ -18,10 +17,13 @@ COPY ./infra/prod/front/serve.json ./build
FROM node:18.16.0-alpine as front FROM node:18.16.0-alpine as front
WORKDIR /app/front WORKDIR /app/front
COPY --from=build /app/front/build ./build COPY --from=build /app/front/build ./build
COPY ./front/scripts/inject-runtime-env.sh /app/front/scripts/inject-runtime-env.sh
RUN yarn global add serve RUN yarn global add serve
LABEL org.opencontainers.image.source=https://github.com/twentyhq/twenty LABEL org.opencontainers.image.source=https://github.com/twentyhq/twenty
LABEL org.opencontainers.image.description="This image provides a consistent and reproducible environment for the frontend." LABEL org.opencontainers.image.description="This image provides a consistent and reproducible environment for the frontend."
CMD ["serve", "build"] CMD ["/bin/sh", "-c", "/app/front/scripts/inject-runtime-env.sh && serve build"]

View File

@ -1,4 +1,4 @@
FROM node:18.16.0-alpine as server FROM node:18.16.0-alpine as build
WORKDIR /app/server WORKDIR /app/server
COPY ./server/package.json ./ COPY ./server/package.json ./
@ -11,6 +11,12 @@ RUN npx prisma generate
RUN yarn build RUN yarn build
FROM node:18.16.0-alpine as server
COPY --from=build /app/server/dist ./dist
WORKDIR /app/server
LABEL org.opencontainers.image.source=https://github.com/twentyhq/twenty LABEL org.opencontainers.image.source=https://github.com/twentyhq/twenty
LABEL org.opencontainers.image.description="This image provides a consistent and reproducible environment for the backend, ensuring it deploys faster and runs the same way regardless of the deployment environment." LABEL org.opencontainers.image.description="This image provides a consistent and reproducible environment for the backend, ensuring it deploys faster and runs the same way regardless of the deployment environment."

View File

@ -14,7 +14,7 @@ services:
name: server name: server
env: docker env: docker
dockerfilePath: ./infra/prod/server/Dockerfile dockerfilePath: ./infra/prod/server/Dockerfile
dockerCommand: "sh -c yarn prisma:migrate && yarn database:setup && node dist/src/main" dockerCommand: "sh -c ./scripts/compute-database-url.sh && yarn prisma:migrate && yarn database:setup && node dist/src/main"
autoDeploy: false autoDeploy: false
envVars: envVars:
- key: FRONT_BASE_URL - key: FRONT_BASE_URL
@ -38,8 +38,6 @@ services:
name: twenty_postgres name: twenty_postgres
type: pserv type: pserv
property: port property: port
- key: PG_DATABASE_URL
value: postgres://twenty:twenty@twenty-postgres:5432/default?connection_limit=1
disk: disk:
name: twenty-disk name: twenty-disk
mountPath: /.local-storage mountPath: /.local-storage

View File

@ -0,0 +1,2 @@
#!/bin/sh
export PG_DATABASE_URL=postgres://twenty:twenty@$PG_DATABASE_HOST:5432/default

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# src/run-integration.sh # scripts/run-integration.sh
DIR="$(cd "$(dirname "$0")" && pwd)" DIR="$(cd "$(dirname "$0")" && pwd)"
source $DIR/set-env-test.sh source $DIR/set-env-test.sh

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# scripts/setenv.sh # scripts/set-env-test.sh
# Get script's directory # Get script's directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"