At the end of this article, you will get the following:
- A PWA with support for install on your devices.
- A React app made with TypeScript.
- Two enviroments on Firebase. You can add more if you need.
- A enviroment for testing and development.
- A production enviroment.
- A testing setup with Jest and enzyme.
Sorry for some disorder with the points. I have problems with the markdown syntax here on dev.to
Prerequirements
You must have installed the following software:
- Node.JS
- Yarn or npm
- A google account
- A gitlab.com account
On the part of knowledge. You only need some knowledge on the use of terminal. You don't need know about React.JS or TypeScript.
Setting up the Firebase projects
Go to https://firebase.google.com/ and sign in with your google account. Click on "Go to console" button. Now, you should see a list with your projects created. If you have not created any before, the list will be empty. You must follow the next instructions to create two projects.
- Add project. This step take a while.
- Click on the project.
- On the sidebar, go to Authentication > User auth methods.
- Enable the methods that you want. I recommend at least email and Google.
- Go to database on the sidebar. Click on "Create database". Ensure that the database is Cloud firestore. The database rules will be updated later. Select a location for your database.
- Repeat the previous steps for "Storage" section. The rules and storage location may be differents than databases.
Now create a second project with the same configuration. The location maybe differents than at the first project, but the rules must be the same.
Initialize the project on local
-
Install the firebase CLI.
sudo npm install -g firebase-tools // or yarn global add firebase-tools
-
Login on the cli with the same account that you create the projects.
firebase login
List the projects created
firebase projects:list
- Create the project
mkdir my-project
cd my-project
firebase init
Select Firestore, Hosting and Storage pressing the Space key and press Enter to confirm. Select "Use an existing project". Select your main (production) project. Select the default options.
- Create the React project
npx create-react-app client --typescript
The project name is up to you, and you can use JavaScript if you want.
- Create the alias projects. Copy and replace the content of your local
.firebaserc
file with
{
"projects": {
"production": "<your-production-project-id>",
"staging": "<your-staging-project-id>"
}
}
- Create a token for production environment. Copy the token and save for later. This is the token for production.
firebase use production
firebase login:ci
Repeat the previous step for staging environment and remember copy the token.
Change the rules. I recommend change the default rules to the following
allow read, write;
With this rule, you can read and write with any control. This can be useful for the firsts steps of the project. The rules will change every time that you deploy. Later you can change the rules for other more secure. Take in consideration that all of your environments share the rules.
Setup the React app
- First of all go to the app folder.
cd client
- Remove the
.git
folder.
rm -rf .git
- Install the needed dependencies.
yarn add dotenv firebase
- Install the types dependencies (Skip this step if you don't use TypeScript).
yarn add -D @types/dotenv @types/firebase
- Inside the
src
folder, create a file namedfirebase.ts
(the name is up to you). And fill with the following code
// firebase.ts
import firebase from 'firebase/app';
import 'firebase/storage';
import 'firebase/firestore';
import 'firebase/auth';
const firebaseConfig = {
apiKey: process.env.REACT_APP_API_KEY,
authDomain: process.env.REACT_APP_AUTH_DOMAIN,
databaseURL: process.env.REACT_APP_DATABASE_URL,
projectId: process.env.REACT_APP_PROJECT_ID,
storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_APP_ID,
measurementId: process.env.REACT_APP_MEASUREMENT_ID
};
firebase.initializeApp(firebaseConfig);
- Create a file named
.env
at the same level of yourpackage.json
. Copy and paste the following code and use your own values. Those values can be extracted for your staging project in firebase. For more info visit https://create-react-app.dev/docs/adding-custom-environment-variables/
REACT_APP_API_KEY=
REACT_APP_AUTH_DOMAIN=
REACT_APP_DATABASE_URL=
REACT_APP_PROJECT_ID=
REACT_APP_STORAGE_BUCKET=
REACT_APP_MESSAGING_SENDER_ID=
REACT_APP_APP_ID=
- Load the enviroment variables. Inside your
src/index.tsx
paste the following code before the render method call.
import { config } from 'dotenv';
config();
- (Optional) This is step is only for made a PWA. A PWA involves many things apart of that, but start replacing
serviceWorker.unregister();
for
serviceWorker.register();
Setup testing
-
Install the development dependencies
yarn add -D enzyme enzyme-adapter-react-16 enzyme-to-json jest jest-enzyme ts-jest
-
Install the types
yarn add -D @types/enzyme @types/enzyme-adapter-react-16 @types/jest
-
(Optional) With the deafult
setup types are added like a dependency instead a dev dependency so copy and paste to the proper place.
- Create a file named
jest.config.js
and write the following code. Note that the extension isjs
notts
.
module.exports = {
roots: ['<rootDir>/src'],
transform: {
'^.+\\.tsx?$': 'ts-jest'
},
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
snapshotSerializers: ['enzyme-to-json/serializer']
};
- Create a test file. The test files should have the following format in their name
my-component.test.tsx
. You can take like reference the test file for App component. For the test step in the workflow, at least a test must be provided.
import React from 'react';
test('Dummy test', () => {
expect(true).toBe(true);
});
This test always pass and it is only provided for the test step.
- Run the test command to check that all works
yarn test
Gitlab setup
- Move the
.gitignore
file content to the root folder file.
- Create a file at the root folder named
.gitlab-ci.yml
and copy the content inside the file
image: node:12.13.1
cache:
key: cache_yarn
paths:
- .cache_yarn
stages:
- install
- build
- test
- deploy
install_client:
stage: install
script:
- cd ./client && yarn install --cache-folder ../.cache_yarn
artifacts:
paths:
- client/node_modules
build_client:
stage: build
script:
- cd ./client && yarn build
dependencies:
- install_client
artifacts:
paths:
- client/build
build_client_staging:
stage: build
script:
- cd ./client && yarn build
dependencies:
- install_client
only:
- staging
environment:
name: staging
artifacts:
paths:
- client/build
build_client_production:
stage: build
script:
- cd ./client && yarn build
dependencies:
- install_client
only:
- production
environment:
name: production
artifacts:
paths:
- client/build
test_client:
stage: test
script:
- cd ./client && yarn test
dependencies:
- install_client
deploy_all_staging:
stage: deploy
script:
- yarn global add firebase-tools --cache-folder ../.cache_yarn
- firebase deploy --token $FIREBASE_DEPLOY_KEY_STAGING --project staging
only:
- staging
environment:
name: staging
dependencies:
- build_client_staging
deploy_all_production:
stage: deploy
script:
- yarn global add firebase-tools --cache-folder ../.cache_yarn
- firebase deploy --token $FIREBASE_DEPLOY_KEY_PRODUCTION --project production
only:
- production
environment:
name: production
dependencies:
- build_client_production
-
Initialize the git repository and add the content
git init git add -A git commit -m "Initial commit"
Create a repository on Gitlab.com
-
Add origin to the repo
git remote add origin git@gitlab.com:<your-gitlab-username>/<your-repo-name>.git
-
Upload the project to Gitlab.com
git push -U origin master
The next steps are done on Gitlab.com
Create two branches on the remote repository. One must be staging and another production
-
Create protected branches.
- Go to Setup > Repository > Protected branches
- Add the production and staging branches
-
Create the enviroment variables
- Go to Setup > CI/CD
- Add the following variables
_______________________________________________
| Key | Scope |
|--------------------------------|------------|
| FIREBASE_DEPLOY_KEY_PRODUCTION | All |
| FIREBASE_DEPLOY_KEY_STAGING | All |
| PUBLIC_URL | All |
| PUBLIC_URL | production |
| REACT_APP_API_KEY | All |
| REACT_APP_API_KEY | production |
| REACT_APP_APP_ID | All |
| REACT_APP_APP_ID | production |
| REACT_APP_AUTH_DOMAIN | All |
| REACT_APP_AUTH_DOMAIN | production |
| REACT_APP_DATABASE_URL | All |
| REACT_APP_DATABASE_URL | production |
| REACT_APP_MESSAGING_SENDER_ID | All |
| REACT_APP_MESSAGING_SENDER_ID | production |
| REACT_APP_PROJECT_ID | All |
| REACT_APP_PROJECT_ID | production |
| REACT_APP_STORAGE_BUCKET | All |
| REACT_APP_STORAGE_BUCKET | production |
-----------------------------------------------
Some considerations: Each variable must be of type Variable, State and Mask must be set to false. Each variable must be have at least the scope All (that is the default scope) to work. These values usually are the correspondant to the staging Firebase project. The production scope values are the values correspondant to the production Firebase project. Note that PUBLIC_URL shuold ends with /. The tokens was generated on previous past, so here it is where we need to use.
How the workflow works
Now we have all piece in their place, so these is how we will work.
- On your local repository, create a branch, and code.
- When your code is ready to merge, upload your branch to remote
git push origin <branch-name>
- Go to Gitlab.com and do a pull request to
master
. 4. Once the multiple steps finish, the code will be merged intomaster
. After that, the test will be passed again. This step is automatic.
Deploy to staging / production
- Do a pull request from
master
tostaging
/production
. 2. Once the merge is succeful, another workflow starts ending on a deploy.
Conclusions
Now you have a production environment and a development environment with React, TypeScript, Jest, Enzyme and Firebase like backend for a complete PWA. You can add more environment if you need.
If you have any doubt, please post on comments.
Top comments (0)