-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #478 from GoogleCloudPlatform/app-scaffold-benoit
App Scaffold
- Loading branch information
Showing
18 changed files
with
463 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# This file specifies files that are *not* uploaded to Google Cloud | ||
# using gcloud. It follows the same syntax as .gitignore, with the addition of | ||
# "#!include" directives (which insert the entries of the given .gitignore-style | ||
# file at that point). | ||
# | ||
# For more information, run: | ||
# $ gcloud topic gcloudignore | ||
# | ||
.gcloudignore | ||
# If you would like to upload your .git directory, .gitignore file or files | ||
# from your .gitignore file, remove the corresponding line | ||
# below: | ||
.git | ||
.gitignore | ||
|
||
# Python pycache: | ||
__pycache__/ | ||
# Ignored by the build system | ||
/setup.cfg |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
all: clean venv | ||
|
||
.PHONY: clean | ||
clean: | ||
@find . -name '*.pyc' -delete | ||
@find . -name '__pycache__' -delete | ||
@find . -name '*egg-info' -type d -exec rm -r {} + | ||
@find . -name '.pytest_cache' -type d -exec rm -r {} + | ||
@rm -rf venv | ||
|
||
.PHONY: venv | ||
venv: | ||
@python3 -m venv venv | ||
@. venv/bin/activate && pip install -U pip && pip install -e . | ||
|
||
.PHONY: auth | ||
auth: | ||
gcloud auth application-default login | ||
|
||
.PHONY: tests | ||
tests: | ||
./scripts/run_tests.sh | ||
|
||
.PHONY: deploy | ||
deploy: clean | ||
./scripts/create_app.sh | ||
./scripts/deploy.sh | ||
|
||
.PHONY: run | ||
run: | ||
./scripts/run_locally.sh |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# Development Workflow | ||
|
||
## Local development | ||
|
||
The workflow described here works from CloudShell or any node with the [gcloud CLI](https://cloud.google.com/sdk/docs/install) has been properly installed and authenticated. | ||
|
||
This means that you can develop your application fully locally on your laptop for example, as long as you have run `make auth` after installing the [gcloud CLI](https://cloud.google.com/sdk/docs/install) on it. | ||
|
||
The first step is to add your `PROJECT` and `BUCKET` names in the following files: | ||
* `./scripts/config.sh` | ||
* `app.yaml` | ||
|
||
For local development, install then the gcloud CLI following [these instructions](https://cloud.google.com/sdk/docs/install). | ||
|
||
Make sure to accept upgrading Python to 3.10 if prompted, then authenticate for local development by running: | ||
|
||
```bash | ||
make auth | ||
``` | ||
|
||
The second step is to create and populate the virtual environment with | ||
|
||
```bash | ||
make venv | ||
``` | ||
After this step you should find a new folder called `venv` containing the virtual environment. | ||
|
||
At this point you should already be able to run the tests by running | ||
```bash | ||
make tests | ||
``` | ||
|
||
To run the app locally, simply run | ||
```bash | ||
make run | ||
``` | ||
|
||
At last to deploy the application on AppEngine run | ||
```bash | ||
make deploy | ||
``` | ||
|
||
**Note:** `make clean` will remove all the built artifacts as long as the virtual environment created by `make venv`. This target is invoked by `make deploy` so that the built artifacts are not uploaded to AppEngine. The down-side is that the virtual environment will need to be recreated after each deployment. | ||
|
||
## Development workflow | ||
|
||
1. Edit the code | ||
1. Run the tests with `make tests` | ||
1. Test the app local with `make run` | ||
1. Deploy the app on AppEngine with `make deploy` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
runtime: python310 | ||
entrypoint: gunicorn -b :$PORT app.server:app | ||
|
||
runtime_config: | ||
operating_system: "ubuntu22" | ||
|
||
env_variables: | ||
BUCKET: "<YOUR_BUCKET>" | ||
LOCATION: "us-central1" | ||
PROJECT: "<YOUR_PROJECT>" |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
""" Flask serving API and small demo UI. | ||
""" | ||
|
||
import logging | ||
|
||
from flask import Flask, jsonify, request, send_file | ||
|
||
app = Flask(__name__) | ||
|
||
|
||
@app.route("/") | ||
def _index(): | ||
"""Serve index.html in the static directory""" | ||
return send_file("static/index.html") | ||
|
||
|
||
@app.route("/myapp", methods=["GET"]) | ||
def _answernaut(): | ||
return jsonify({"answer": request.args["query"]}) | ||
|
||
|
||
@app.errorhandler(500) | ||
def _server_error(e): | ||
"""Serves a formatted message on-error""" | ||
logging.exception("An error occurred during a request.") | ||
return ( | ||
f"An internal error occurred: <pre>{e}</pre><br>", | ||
500, | ||
) | ||
|
||
|
||
if __name__ == "__main__": | ||
# This is used when running locally. Gunicorn is used to run the | ||
# application on Google App Engine. See entrypoint in app.yaml. | ||
app.run(host="127.0.0.1", port=8080, debug=True) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<!doctype html> | ||
<html> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<link rel="stylesheet" href="/static/main.css"> | ||
<link rel="icon" type"image/x-icon" href="/static/asl.png"> | ||
</head> | ||
<body> | ||
<div class="body"> | ||
|
||
<div class="logo"> | ||
<img src="/static/asl.png" alt="ASL Logo" width="100" height="100"> | ||
</div> | ||
|
||
<form action="" id="query-form" class="form"> | ||
<input type="text" id="query-input" class="input"></textarea> | ||
<button id="ask-button" type="submit" class="ask">Ask</button> | ||
</form> | ||
|
||
<div id="query-answer" class="answer"></div> | ||
<div id="spinner" class="lds-spinner"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div> | ||
|
||
</div> | ||
|
||
<script type="text/javascript" src="/static/main.js"></script> | ||
|
||
|
||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
|
||
*, ::after, ::before { | ||
box-sizing: border-box; | ||
border-width: 0; | ||
border-style: solid; | ||
border-color: #e5e7eb; | ||
font-size: 1rem; | ||
font-family: ui-sans-serif, system-ui, -apple-system, "system-ui", "Segoe UI"; | ||
} | ||
|
||
.body { | ||
padding: 4rem; | ||
justify-content: center; | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
width: 100vw; | ||
height: 100vh; | ||
} | ||
|
||
.logo { | ||
margin-bottom: 2.5rem; | ||
} | ||
|
||
.form { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
width: 50%; | ||
margin-bottom: 3rem; | ||
} | ||
|
||
.input { | ||
padding-left: 0.5rem; | ||
padding-right: 0.5rem; | ||
border-width: 1px; | ||
border-radius: 0.5rem; | ||
width: 100%; | ||
height: 2rem; | ||
} | ||
|
||
.ask { | ||
padding-top: 0.25rem; | ||
padding-bottom: 0.25rem; | ||
padding-left: 0.5rem; | ||
padding-right: 0.5rem; | ||
border-width: 1px; | ||
border-color: rgb(191 219 254); | ||
border-radius: 0.5rem; | ||
margin-left: 0.75rem; | ||
margin-top: 2rem; | ||
width: 4rem; | ||
background-color: rgb(219 234 254); | ||
} | ||
|
||
button.ask:hover { | ||
background-color: rgb(3 105 161); | ||
} | ||
|
||
.answer { | ||
display: flex; | ||
padding: 1.25rem; | ||
background-color: rgb(219 234 254); | ||
border-color: rgb(191 219 254); | ||
border-radius: 0.5rem; | ||
width: 50%; | ||
line-height: 1.4rem; | ||
display: none; | ||
overflow-y: auto; | ||
} | ||
|
||
.hide { | ||
display: none !important; | ||
} | ||
|
||
.show { | ||
display: block !important; | ||
} | ||
|
||
|
||
/* Spinner */ | ||
|
||
.lds-spinner { | ||
color: official; | ||
display: inline-block; | ||
position: relative; | ||
width: 80px; | ||
height: 80px; | ||
display: none; | ||
} | ||
.lds-spinner div { | ||
transform-origin: 40px 40px; | ||
animation: lds-spinner 1.2s linear infinite; | ||
} | ||
.lds-spinner div:after { | ||
content: " "; | ||
display: block; | ||
position: absolute; | ||
top: 3px; | ||
left: 37px; | ||
width: 6px; | ||
height: 18px; | ||
border-radius: 20%; | ||
background: rgb(66,132,243); | ||
} | ||
.lds-spinner div:nth-child(1) { | ||
transform: rotate(0deg); | ||
animation-delay: -1.1s; | ||
} | ||
.lds-spinner div:nth-child(2) { | ||
transform: rotate(30deg); | ||
animation-delay: -1s; | ||
} | ||
.lds-spinner div:nth-child(3) { | ||
transform: rotate(60deg); | ||
animation-delay: -0.9s; | ||
} | ||
.lds-spinner div:nth-child(4) { | ||
transform: rotate(90deg); | ||
animation-delay: -0.8s; | ||
} | ||
.lds-spinner div:nth-child(5) { | ||
transform: rotate(120deg); | ||
animation-delay: -0.7s; | ||
} | ||
.lds-spinner div:nth-child(6) { | ||
transform: rotate(150deg); | ||
animation-delay: -0.6s; | ||
} | ||
.lds-spinner div:nth-child(7) { | ||
transform: rotate(180deg); | ||
animation-delay: -0.5s; | ||
} | ||
.lds-spinner div:nth-child(8) { | ||
transform: rotate(210deg); | ||
animation-delay: -0.4s; | ||
} | ||
.lds-spinner div:nth-child(9) { | ||
transform: rotate(240deg); | ||
animation-delay: -0.3s; | ||
} | ||
.lds-spinner div:nth-child(10) { | ||
transform: rotate(270deg); | ||
animation-delay: -0.2s; | ||
} | ||
.lds-spinner div:nth-child(11) { | ||
transform: rotate(300deg); | ||
animation-delay: -0.1s; | ||
} | ||
.lds-spinner div:nth-child(12) { | ||
transform: rotate(330deg); | ||
animation-delay: 0s; | ||
} | ||
@keyframes lds-spinner { | ||
0% { | ||
opacity: 1; | ||
} | ||
100% { | ||
opacity: 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
async function queryApi(prompt) { | ||
const endpoint = `/myapp?query=${prompt}` | ||
const response = await fetch(endpoint) | ||
const answer = await response.json() | ||
return answer | ||
} | ||
|
||
function displayAnswer(answer) { | ||
const queryAnswer = document.getElementById("query-answer") | ||
queryAnswer.innerHTML = `<md-block> ${answer} </md-block>` | ||
} | ||
|
||
function getPrompt() { | ||
const queryInput = document.getElementById("query-input") | ||
return queryInput.value | ||
} | ||
|
||
function getAnswer() { | ||
const spinner = document.getElementById("spinner") | ||
const queryAnswer = document.getElementById("query-answer") | ||
queryAnswer.classList.remove("show") | ||
spinner.classList.add("show") | ||
const prompt = getPrompt() | ||
queryApi(prompt).then( | ||
response => { | ||
spinner.classList.remove("show") | ||
queryAnswer.classList.add("show") | ||
displayAnswer(response.answer) | ||
} | ||
) | ||
|
||
} | ||
|
||
function init() { | ||
const queryForm = document.getElementById("query-form") | ||
queryForm.addEventListener("submit", e => { | ||
e.preventDefault() | ||
getAnswer() | ||
}) | ||
|
||
const queryInput = document.getElementById("query-input") | ||
queryInput.addEventListener("keyup", e => { | ||
e.preventDefault() | ||
if (e.KeyCode === 13){ | ||
document.getElementById("ask-button").click() | ||
} | ||
}) | ||
|
||
} | ||
|
||
init() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Flask==3.0.0 | ||
gunicorn==20.1.0 | ||
pre-commit==3.7.1 | ||
pytest==7.0.1 |
Oops, something went wrong.