diff --git a/.cloudbuild.yaml b/.cloudbuild.yaml
new file mode 100644
index 0000000..7837d15
--- /dev/null
+++ b/.cloudbuild.yaml
@@ -0,0 +1,29 @@
+steps:
+ - id: 'build-image'
+ name: 'gcr.io/cloud-builders/docker'
+ args:
+ - 'build'
+ - '-t'
+ - 'eu.gcr.io/$PROJECT_ID/$REPO_NAME/$BRANCH_NAME/$REPO_NAME:$SHORT_SHA'
+ - '-t'
+ - 'eu.gcr.io/$PROJECT_ID/$REPO_NAME/$BRANCH_NAME/$REPO_NAME:latest'
+ - '.'
+ - '-f'
+ - './Dockerfile'
+ timeout: 1200s
+ - id: 'push-to-registry'
+ name: 'gcr.io/cloud-builders/docker'
+ args:
+ - 'push'
+ - 'eu.gcr.io/$PROJECT_ID/$REPO_NAME/$BRANCH_NAME/$REPO_NAME'
+ - id: 'deploy-to-gke'
+ name: 'gcr.io/cloud-builders/gcloud'
+ env:
+ - 'KUBECONFIG=/.kube/config'
+ entrypoint: 'bash'
+ args:
+ - '-c'
+ - |
+ gcloud container clusters get-credentials soils-revealed-cluster --project=$PROJECT_ID --zone=europe-west4-a
+ kubectl set image deployment/$REPO_NAME --namespace=$BRANCH_NAME soils-revealed=eu.gcr.io/$PROJECT_ID/$REPO_NAME/$BRANCH_NAME/$REPO_NAME:$SHORT_SHA
+ kubectl rollout restart deployment $REPO_NAME --namespace=$BRANCH_NAME
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..9eadd8e
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,18 @@
+# ignore folder on copy (more for local build)
+*.md
+.git
+.cache
+.history
+node_modules
+npm-debug.log
+.prettierrc
+.gitignore
+.github
+.git
+.eslintrc
+.env.default
+.editorconfig
+.dockerignore
+.env
+.Dockerfile
+.cloudbuild.yaml
diff --git a/.env.default b/.env.default
index 7c1e2ce..cbda645 100644
--- a/.env.default
+++ b/.env.default
@@ -1,11 +1,13 @@
-PORT=3000
+PORT=3001
MAPBOX_API_KEY=
API_URL=
ANALYSIS_API_URL=
-DEPLOYMENT_KEY=
GOOGLE_ANALYTICS_KEY=
AWS_REGION=
AWS_BUCKET_NAME=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_MAX_Z_TILE_STORAGE=
+DEPLOYMENT_KEY=
+AIRTABLE_API_KEY=
+AIRTABLE_USER_ID=
diff --git a/.github/workflows/production.yml b/.github/workflows/production.yml
deleted file mode 100644
index 81c7fa3..0000000
--- a/.github/workflows/production.yml
+++ /dev/null
@@ -1,61 +0,0 @@
-name: Production build
-
-on:
- push:
- branches:
- - master
-
-jobs:
- deploy:
- runs-on: ubuntu-latest
-
- steps:
- - name: Run SSH commands
- # Documentation: https://github.com/marketplace/actions/ssh-remote-commands
- uses: appleboy/ssh-action@master
- env:
- NODE_PORT: 3000
- MAPBOX_API_KEY: pk.eyJ1IjoidG5jc29pbHNjaWVuY2UiLCJhIjoiY2s5Y3dweXg0MDBlZjNkbXE5ZGk4Y294biJ9.nGYVoUKe7Z8MJoi5GwCGjg
- API_URL: http://35.233.41.65/user/skydipper/api/v2
- ANALYSIS_API_URL: https://soilsrevealed.org/api/v1/analysis
- GEE_KEY: ${{ secrets.GEE_KEY }}
- GOOGLE_ANALYTICS_KEY: UA-179817360-1
- AWS_REGION: ${{ secrets.AWS_REGION }}
- AWS_BUCKET_NAME: ${{ secrets.AWS_BUCKET_NAME }}
- AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
- AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- AWS_MAX_Z_TILE_STORAGE: 5
- with:
- host: ${{ secrets.SSH_HOST }}
- key: ${{ secrets.SSH_KEY }}
- port: ${{ secrets.SSH_PORT }}
- username: ${{ secrets.SSH_USER }}
- envs: NODE_PORT,MAPBOX_API_KEY,API_URL,ANALYSIS_API_URL,GEE_KEY,GOOGLE_ANALYTICS_KEY,AWS_REGION,AWS_BUCKET_NAME,AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_MAX_Z_TILE_STORAGE
- script: |
- echo '> Source nvm'
- export NVM_DIR=~/.nvm
- source ~/.nvm/nvm.sh
- echo '> Export the path'
- export PATH=$PATH:/home/ubuntu/.nvm/versions/node/v12.16.1/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin
- echo '> Kill previous node server'
- pm2 stop production
- echo '> Open production folder'
- cd ~/soils-revealed
- echo '> Pull from git'
- git fetch origin master
- git reset --hard origin/master
- echo '> Use correct node version'
- nvm install
- nvm use
- echo '> Install dependencies'
- yarn install --frozen-lockfile
- echo '> Generate a deployment key'
- DEPLOYMENT_KEY=`date +%s`
- echo '> Create .env file'
- echo -e "PORT=$NODE_PORT\nMAPBOX_API_KEY=$MAPBOX_API_KEY\nAPI_URL=$API_URL\nANALYSIS_API_URL=$ANALYSIS_API_URL\nDEPLOYMENT_KEY=$DEPLOYMENT_KEY\nGOOGLE_ANALYTICS_KEY=$GOOGLE_ANALYTICS_KEY\nAWS_REGION=$AWS_REGION\nAWS_BUCKET_NAME=$AWS_BUCKET_NAME\nAWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID\nAWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY\nAWS_MAX_Z_TILE_STORAGE=$AWS_MAX_Z_TILE_STORAGE" > .env
- echo '> Create gee.key.json file'
- echo $GEE_KEY > gee.key.json
- echo '> Build the app'
- yarn build
- echo '> Run the server'
- pm2 start production
diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml
deleted file mode 100644
index b102368..0000000
--- a/.github/workflows/staging.yml
+++ /dev/null
@@ -1,61 +0,0 @@
-name: Staging build
-
-on:
- push:
- branches:
- - develop
-
-jobs:
- deploy:
- runs-on: ubuntu-latest
-
- steps:
- - name: Run SSH commands
- # Documentation: https://github.com/marketplace/actions/ssh-remote-commands
- uses: appleboy/ssh-action@master
- env:
- NODE_PORT: 3001
- MAPBOX_API_KEY: pk.eyJ1IjoidG5jc29pbHNjaWVuY2UiLCJhIjoiY2s5Y3dweXg0MDBlZjNkbXE5ZGk4Y294biJ9.nGYVoUKe7Z8MJoi5GwCGjg
- API_URL: http://35.233.41.65/user/skydipper/api/v2
- ANALYSIS_API_URL: https://soilsrevealed.org/api/v1/analysis
- GEE_KEY: ${{ secrets.GEE_KEY }}
- GOOGLE_ANALYTICS_KEY:
- AWS_REGION: ${{ secrets.AWS_REGION }}
- AWS_BUCKET_NAME: ${{ secrets.AWS_BUCKET_NAME }}
- AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
- AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- AWS_MAX_Z_TILE_STORAGE: 5
- with:
- host: ${{ secrets.SSH_HOST }}
- key: ${{ secrets.SSH_KEY }}
- port: ${{ secrets.SSH_PORT }}
- username: ${{ secrets.SSH_USER }}
- envs: NODE_PORT,MAPBOX_API_KEY,API_URL,ANALYSIS_API_URL,GEE_KEY,GOOGLE_ANALYTICS_KEY,AWS_REGION,AWS_BUCKET_NAME,AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_MAX_Z_TILE_STORAGE
- script: |
- echo '> Source nvm'
- export NVM_DIR=~/.nvm
- source ~/.nvm/nvm.sh
- echo '> Export the path'
- export PATH=$PATH:/home/ubuntu/.nvm/versions/node/v12.16.1/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin
- echo '> Kill previous node server'
- pm2 stop staging
- echo '> Open staging folder'
- cd ~/soils-revealed-staging
- echo '> Pull from git'
- git fetch origin develop
- git reset --hard origin/develop
- echo '> Use correct node version'
- nvm install
- nvm use
- echo '> Install dependencies'
- yarn install --frozen-lockfile
- echo '> Generate a deployment key'
- DEPLOYMENT_KEY=`date +%s`
- echo '> Create .env file'
- echo -e "PORT=$NODE_PORT\nMAPBOX_API_KEY=$MAPBOX_API_KEY\nAPI_URL=$API_URL\nANALYSIS_API_URL=$ANALYSIS_API_URL\nDEPLOYMENT_KEY=$DEPLOYMENT_KEY\nGOOGLE_ANALYTICS_KEY=$GOOGLE_ANALYTICS_KEY\nAWS_REGION=$AWS_REGION\nAWS_BUCKET_NAME=$AWS_BUCKET_NAME\nAWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID\nAWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY\nAWS_MAX_Z_TILE_STORAGE=$AWS_MAX_Z_TILE_STORAGE" > .env
- echo '> Create gee.key.json file'
- echo $GEE_KEY > gee.key.json
- echo '> Build the app'
- yarn build
- echo '> Run the server'
- pm2 start staging
diff --git a/.gitignore b/.gitignore
index 756b8f6..d89545e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,3 +26,8 @@ yarn-error.log*
# keys
gee.key.json
+
+#VScode
+.history/
+
+
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..ede5796
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,29 @@
+FROM docker.io/debian:stable-slim@sha256:a939c03c4d3e3f53e3ef4ef6e75cb681a3ad56537842f95bf89755da86559b13
+
+# Source: https://www.kabisa.nl/tech/nvm-in-docker/
+# docker build --force-rm --no-cache -t soils-revealed:latest .
+# docker run -p3001:3001 --env-file .env soils-revealed:latest
+
+SHELL ["/bin/bash","-l","-c"]
+ENV DEBIAN_FRONTEND noninteractive
+
+RUN apt-get update \
+ && apt-get install -y tini curl \
+ && rm -rf /var/lib/apt/lists/*
+
+RUN mkdir /soils-revealed
+WORKDIR /soils-revealed
+COPY . .
+
+RUN curl --silent -o- https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
+RUN nvm install && nvm use
+RUN echo -e "Implementing node: $(node --version)\nFrom: $(which node)"
+
+RUN npm install -g yarn
+RUN npm install -g pm2
+RUN yarn install --frozen-lockfile
+
+RUN DEPLOYMENT_KEY=$(date +%s) && echo "DEPLOYMENT_KEY=$DEPLOYMENT_KEY" > .env
+
+ENTRYPOINT ["/usr/bin/tini","-g","--"]
+CMD ["/soils-revealed/run.sh","production"]
diff --git a/README.md b/README.md
index a21af21..033cd36 100644
--- a/README.md
+++ b/README.md
@@ -17,14 +17,15 @@ In order to start modifying the app, please make sure to correctly configure you
5. Use the correct Node.js version for this app by running `nvm use`; if you didn't install NVM (step 2), then manually install the Node.js version described in `.nvmrc`
6. Install the dependencies: `yarn`
7. Create a `.env` file at the root of the project by copying `.env.default` and giving a value for each of the variables (see next section for details)
-8. Create a `gee.key.json` file at the root of the project with the Google Earth Engine's private key inside
+8. Create a gee.key.json file at the root of the project with the Google Earth Engine's private key inside.
+
9. Run the server: `yarn dev`
You can access a hot-reloaded version of the app on [http://localhost:3000](http://localhost:3000).
The application is built using [React](https://reactjs.org/) and the framework [Next.js](https://nextjs.org/). The styles use [Sass](https://sass-lang.com/) and the [Bootstrap](https://getbootstrap.com/) framework.
-A continuous deployment system is in place. Each time you push to the `master` branch, the application is deployed to production through a GitHub Action defined in `.github/workflows/production.yml`. Each time you push to `develop`, the application is deployed to staging through an action defined in `.github/workflows/staging.yml`. You can see the status of the build in the “Actions” tab of the repository on GitHub.
+A continuous deployment system is in place. Each time you push to the `master` branch, the application is deployed to production. Github will trigger an event on google cloud run and make a deployment. The same for the `develop` branch.
## Environment variables
@@ -44,18 +45,42 @@ Below is a description of each of the keys.
| AWS_ACCESS_KEY_ID | Access key ID of the AWS server storing the tiles of the soils layers |
| AWS_SECRET_ACCESS_KEY | Secret access key of the AWS server storing the tiles of the soils layers |
| AWS_MAX_Z_TILE_STORAGE | Maximum zoom at which tiles generated on-the-fly will be saved in the AWS S3 bucket |
+| AIRTABLE_API_KEY | Secret access key for [Airtable](https://airtable.com/) |
+| AIRTABLE_USER_ID | Airtable User ID |
+
## Deployment
-As explained before, the application is automatically deployed to staging when pushing new changes to the `develop` branch, and deployed to production when pushing changes to `master`. This is achieved through GitHub Actions defined in `.github/workflows`.
+### Local computer
+
+It is possible to run a local test deployment using the docker image. Docker implement an agnostic build and then during run it will pickup the container's env variables, and properly set the system
+
+`run.sh` accepts 2 arguments: production or develop. Production argument will run `yarn start` and the code will production ready, while `develop` runs nodejs in development mode, necessary if you are testing content and changes.
+
+```bash
+docker build --force-rm --no-cache -t soils-revealed:latest .
+docker run -p3001:3001 --env-file .env soils-revealed:latest /soils-revealed/run.sh production
+```
+
+**Note:** We have created a `.env` file on the project root with all variables, and this will be used to run nodejs in production mode. This is a deployment to test nodejs in production mode.
+
+**Note:** Dockerfile has `CMD` implementing production.
+
-When an action is executed, it connects via SSH to the server hosting the application. The server's credentials are stored in GitHub's “secrets” vault. A script is then executed: the running instance of the application is stopped, the code is pulled, the correct version of node is selected, the dependencies are installed, a local `.env` file is generated, a local `gee.key.json` file is generated, and the application is restarted.
+### Google GKE
-The `.env` file is programmatically generated on the server because it differs for each environment. Some of the keys are hard coded in the `.github/workflows/XXX.yml` file and others are pulled from GitHub's “secrets” vault.
+Public deployment is based on Google Cloud build and file `.cloudbuild.yaml`. Up on push to `master` or `develop`, the following steps will happen:
-The `gee.key.json` file contains the credentials for the Google Earth Engine library. It is also programmatically generated as its value is stored in GitHub's “secrets” vault.
+1. Github will trigger a Google Cloud run trigger
+2. Google cloud will pull the branch content.
+3. Docker build will be iniciated, using `Dockerfile` and `.cloudbuild.yaml`
+4. After completed Docker image is stored on a private repository, using tags `latest` and `$SHORT_SHA`
+5. Google Cloud build will update the image on GKE and make a `kubectl rollout restart`
+6. GKE contains a specific `ConfigMap` with all .env necessary for deployment.
+7. `gee.key.json` is added to the pods using a `ConfigMap` mount
+
-Overall, deploying to either environment takes between 1 to 2 minutes to complete.
+Overall, deploying to either environment takes between 5 to 10 minutes to complete. If deployment is not successful GKE will continue implementing the previous deployment.
## Architecture
diff --git a/components/explore/component.js b/components/explore/component.js
index fe1ba2b..e0a9d70 100644
--- a/components/explore/component.js
+++ b/components/explore/component.js
@@ -6,6 +6,7 @@ import throttle from 'lodash/debounce';
import { Router } from 'lib/routes';
import { logEvent } from 'utils/analytics';
+import { isFirstVisit } from 'utils/explore';
import { useHasMounted, useDesktop } from 'utils/hooks';
import { toggleBasemap, toggleLabels, toggleRoads } from 'utils/map';
import {
@@ -28,6 +29,7 @@ import InfoModal from './info-modal';
import InteractiveFeaturePopup from './interactive-feature-popup';
import DrawBoard from './draw-board';
import MapContainer from './map-container';
+import UserModal from 'components/user-modal';
import './style.scss';
@@ -76,6 +78,14 @@ const Explore = ({
const [interactiveFeatures, setInteractiveFeatures] = useState(null);
const [showTour, setShowTour] = useState(false);
+ // User recruitment modal. This modal should appear just the first time the user
+ // visits the map section
+ const [userModalOpen, setUserModalOpen] = useState(isFirstVisit());
+
+ const handleModalClose = () => {
+ setUserModalOpen(false);
+ };
+
// When the user clicks the popup's button that triggers its close, the map also receives the
// event and it opens a new popup right after
// This is a bug of react-map-gl's library
@@ -222,6 +232,7 @@ const Explore = ({
className="c-explore"
style={isDesktop ? { backgroundColor: BASEMAPS[basemap].backgroundColor } : undefined}
>
+