Skip to content

Commit

Permalink
refactor: workflow, frontend app, and frontend build
Browse files Browse the repository at this point in the history
* fixes create react app bug: facebook/create-react-app#8688
* adds styling to frontend app
* improves frontend dockerfile
* adds additional commands to makefile and README
  • Loading branch information
ivorscott committed Mar 23, 2020
1 parent 6af7142 commit 7e3b5c2
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 109 deletions.
24 changes: 19 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

## The Ultimate Go and React Development Setup with Docker (Part 2)

### Building A Complete Example
### Building A Complete API Example

This repository is paired with a [blog post](https://blog.ivorscott.com/ultimate-go-react-development-setup-with-docker-part2). If you follow along, the project starter is available under the `part2_starter` branch. Part 2 is heavily influenced by [Ardan labs service training](https://github.com/ardanlabs/service-training). I highly recommend their [courses](https://education.ardanlabs.com/).
This repository is paired with a [blog post](https://blog.ivorscott.com/ultimate-go-react-development-setup-with-docker-part2). Part 2 is heavily influenced by [Ardan labs service training](https://github.com/ardanlabs/service-training). I highly recommend their [courses](https://education.ardanlabs.com/).

[Previous blog post](https://blog.ivorscott.com/ultimate-go-react-development-setup-with-docker)

Expand Down Expand Up @@ -379,9 +379,17 @@ make api # develop the api with live reloading
make client # develop the client app in a separate terminal
```

This approach to development uses containers entirely.
![Minion](docs/run.png)

6 - **Idiomatic Go development** (container free go api)
6 - Navigate to https://localhost:4000/v1/products and https://localhost:3000 in two separate tabs. This approach to development uses containers entirely.

**Note:**

To replicate the production environment as much as possible locally, we use self-signed certificates.

In your browser, you may see a warning and need to click a link to proceed to the requested page. This is common when using self-signed certificates.

7 - **Idiomatic Go development** (container free go api)

Another option is to only containerize the client and database. This approach
allows you to work with the go api in an idiomatic fashion, with command line flags
Expand Down Expand Up @@ -445,6 +453,10 @@ make debug-api # use delve on the same api in a separate container (no live relo

make debug-db # use pgcli to inspect postgres db

make rm # remove all containers

make rmi # remove all images

make exec user="..." service="..." cmd="..." # execute command in running container

make tidy # clean up unused api dependencies
Expand All @@ -453,6 +465,8 @@ make test-api # run api tests

make test-client # run client tests

make test-client-watch # run client tests and watch

make migration <name> # create a migration

make version # print current migration version
Expand Down Expand Up @@ -519,7 +533,7 @@ Run the debuggable api. Set a break point on a route handler. Click 'Launch remo

</details>

### Building A Complete Example
### Building A Complete API Example

The Ultimate Go and React Development Setup with Docker (Part 2)

Expand Down
51 changes: 23 additions & 28 deletions client/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,74 +7,69 @@ WORKDIR /client
# 3. Copy both package.json and package-lock.json into /client in the image's filesystem
COPY package*.json ./

# 4. Install only the production node_modules and clean up the cache
RUN npm ci \
&& npm cache clean --force

# 5. Extend the base stage and create a new stage named dev
# 4. Extend the base stage and create a new stage named dev
FROM base as dev

# 6. Set the NODE_ENV and PATH Environment variables
# 5. Set the NODE_ENV and PATH Environment variables
ENV NODE_ENV=development
ENV PATH /client/node_modules/.bin:$PATH

# 7. Provide meta data about the port the container must expose
# 6. Provide meta data about the port the container must expose
EXPOSE 3000

# 8. Create a new /app directory in /client
# 7. Create a new /app directory in /client
RUN mkdir /client/app

# 9. Install development dependencies
RUN npm i --only=development \
&& npm cache clean --force
# 8. Install development dependencies
RUN npm install && npm cache clean --force

# 10. Print npm configuration for debugging purposes
# 9. Print npm configuration for debugging purposes
RUN npm config list

# 11. Set the working directory to /client/app
# 10. Set the working directory to /client/app
WORKDIR /client/app

# 12. Provide the default command for the development container
# 11. Provide the default command for the development container
CMD ["npm", "run", "start"]

# 13. Extend the dev stage and create a new stage called test
# 12. Extend the dev stage and create a new stage called test
FROM dev as test

# 14. Copy the remainder of the client folder source code into the image's filesystem
# 13. Copy the remainder of the client folder source code into the image's filesystem
COPY . .

# 15. Run node_module vulnerability checks
# 14. Run node_module vulnerability checks
RUN npm audit

# 16. Run unit tests in CI
# 15. Run unit tests in CI
RUN CI=true npm test --env=jsdom

# 17. Extend the test stage and create a new stage named build-stage
# 16. Extend the test stage and create a new stage named build-stage
FROM test as build-stage

# 18. Build the production static assets
# 17. Build the production static assets
RUN npm run build

# 19. Install aquasecurity's trivy for robust image scanning
FROM aquasec/trivy:0.4.3 as trivy
# 18. Install aquasecurity's trivy for robust image scanning
FROM aquasec/trivy:0.4.4 as trivy

# 20. Scan the nginx alpine image before production use
# 19. Scan the nginx alpine image before production use
RUN trivy nginx:1.17-alpine && \
echo "No image vulnerabilities" > result

# 21. Extend the nginx apline image and create a new stage named prod
# 20. Extend the nginx apline image and create a new stage named prod
FROM nginx:1.17-alpine as prod

# 22. Copy only the files we want from a few stages into the prod stage
# 21. Copy only the files we want from a few stages into the prod stage
COPY --from=trivy result secure
COPY --from=build-stage /client/app/build /usr/share/nginx/html

# 23. Copy non-root user nginx configuration
# 22. Copy non-root user nginx configuration
# https://hub.docker.com/_/nginx
COPY --from=build-stage /client/app/nginx /etc/nginx/

# 24. Provide meta data about the port the container must expose
# 23. Provide meta data about the port the container must expose
EXPOSE 80

# 25. Define how Docker should test the container to check that it is still working
# 24. Define how Docker should test the container to check that it is still working
HEALTHCHECK CMD [ "wget", "-q", "0.0.0.0:80" ]
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"workbox-webpack-plugin": "4.3.1"
},
"scripts": {
"start": "node scripts/start.js",
"start": "HTTPS=true REACT_APP_API_URL=https://localhost:4000/v1 node scripts/start.js",
"build": "node scripts/build.js",
"test": "node scripts/test.js"
},
Expand Down
57 changes: 7 additions & 50 deletions client/src/App/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,16 @@ body {
box-sizing: border-box;
// https://css-tricks.com/snippets/css/font-shorthand/
font: normal 400 24px/1.7 "Lato", Helvetica, sans-serif;
background: #000;
color: #777;
padding: 30px;
}

.header {
position: relative;
height: 85vh;
background-image: linear-gradient(
to right bottom,
rgba(126, 2123, 111, 0.8),
rgba(209, 73, 31, 0.6)
rgba(126, 212, 111, 0.8),
rgba(209, 73, 31, 0.8)
),
url("http://getwallpapers.com/wallpaper/full/d/3/a/547521.jpg");
background-size: cover;
Expand Down Expand Up @@ -133,54 +131,13 @@ body {
}
}

.products-feature {
.main {
position: relative;
top: -650px;
margin: 100px;
top: -500px;
text-align: center;
color: #fff;

header {
font-size: 36px;
font-weight: 700;
text-align: center;
margin-bottom: 46px;
}

.products {
display: flex;
flex: 1;
justify-content: center;
&__card {
width: 100%;
min-height: 300px;
max-width: 300px;
margin: 10px;
padding: 12px;
background-color: #fff;
border-radius: 8px;
color: #444;
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25),
0 10px 10px rgba(0, 0, 0, 0.22);
}
.a {
position: relative;
top: 0px;
animation: moveInBottom 0.65s ease-out 1s;
animation-fill-mode: backwards;
}
.b {
position: relative;
top: 40px;
animation: moveInBottom 0.65s ease-out 1.2s;
animation-fill-mode: backwards;
}
.c {
position: relative;
top: 80px;
animation: moveInBottom 0.65s ease-out 1.4s;
animation-fill-mode: backwards;
}
}
animation: moveInBottom 0.65s ease-out 1.4s;
animation-fill-mode: backwards;
}

@keyframes moveInLeft {
Expand Down
12 changes: 6 additions & 6 deletions client/src/App/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
import React from "react";
import { render } from "@testing-library/react";
import App from "./App";

test('renders learn react link', () => {
test("renders learn react link", () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
const button = getByText(/Sign Up/i);
expect(button).toBeInTheDocument();
});
6 changes: 5 additions & 1 deletion client/src/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ class App extends React.Component<{}, State> {
subtitle="Second hand games"
callToActionText="Sign Up"
/>
<Products products={this.state.products} />
<div className="main">
<h1>The Best Prices</h1>
<h3>Gaming Is Our Passion. Get 20% Off Any 2nd Purchase!</h3>
<Products products={this.state.products} />
</div>
</div>
);
}
Expand Down
26 changes: 26 additions & 0 deletions client/src/App/Products/Products.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.products-feature {
color: #fff;
margin-top: 40px;

.products {
display: flex;
flex: 1;
justify-content: center;
&__card {
display: flex;
flex: 1;
flex-direction: column;
justify-content: center;
width: 100%;
min-height: 300px;
max-width: 300px;
margin: 10px;
padding: 12px;
background-color: #fff;
border-radius: 8px;
color: #444;
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25),
0 10px 10px rgba(0, 0, 0, 0.22);
}
}
}
22 changes: 6 additions & 16 deletions client/src/App/Products/Products.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,16 @@
import React from "react";
import { IProduct } from "./types";

const classes = ["a", "b", "c"];
import "./Products.scss";

const Products: React.FC<{ products: IProduct[] }> = ({ products }) => (
<main className="products-feature">
<header>Store</header>
<div className="products">
{products.map((product, index) => {
{products.map(product => {
return (
index < 3 && (
<div
key={product.id}
className={`products__card ${classes[index]}`}
>
<b className="products__card-name">{product.name}</b>
<p className="products__card-description">
{product.description}
</p>
<p className="products__card-price">{product.price}</p>
</div>
)
<div key={product.id} className="products__card">
<b className="products__card-name">{product.name}</b>
<p className="products__card-price">{product.price}</p>
</div>
);
})}
</div>
Expand Down
7 changes: 6 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ services:
- ./api:/api
networks:
- postgres-net
# swap commands to disable live reload
# command: go run ./cmd/api
command: CompileDaemon --build="go build -o main ./cmd/api" -log-prefix=false --command=./main

Expand All @@ -33,9 +34,13 @@ services:
environment:
HTTPS: "true"
REACT_APP_API_URL: https://localhost:$API_PORT/v1
CHOKIDAR_USEPOLLING: "true"
ports:
- $CLIENT_PORT:$CLIENT_PORT
# fixes bug in react-scripts/start.js found in latest create react app release
# https://github.com/facebook/create-react-app/issues/8688
tty: true
stdin_open: true
volumes:
- ./client:/client/app
- /client/app/node_modules
Expand Down Expand Up @@ -63,7 +68,7 @@ services:
networks:
- postgres-net
# run a container without the default seccomp profile
# https://github.com/go-delve/delve/issues/515
# https://github.com/go-delve/delve/issues/515#issuecomment-214911481
security_opt:
- "seccomp:unconfined"
tty: true
Expand Down
Binary file added docs/run.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 7e3b5c2

Please sign in to comment.