├── .devcontainer
├── Dockerfile
└── devcontainer.json
├── .github
└── workflows
│ ├── docker_image.yml
│ └── web_app.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── SECURITY.md
├── SUPPORT.md
├── images
├── api-running.png
├── fast-api.png
├── try-it-out.png
└── video-banner.gif
├── requirements.txt
├── translations
├── es
│ └── README.md
└── pt-BR
│ └── README.md
└── webapp
├── main.py
└── static
├── bootstrap.min.css
└── index.html
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.245.0/containers/python-3/.devcontainer/base.Dockerfile
2 |
3 | # [Choice] Python version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.10, 3.9, 3.8, 3.7, 3.6, 3-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye, 3-buster, 3.10-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster
4 | ARG VARIANT="3.10-bullseye"
5 | FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}
6 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.245.0/containers/python-3
3 | {
4 | "name": "Python 3",
5 | "forwardPorts": [8000],
6 | "build": {
7 | "dockerfile": "Dockerfile",
8 | "context": "..",
9 | "args": {
10 | // Update 'VARIANT' to pick a Python version: 3, 3.10, 3.9, 3.8, 3.7, 3.6
11 | // Append -bullseye or -buster to pin to an OS version.
12 | // Use -bullseye variants on local on arm64/Apple Silicon.
13 | "VARIANT": "3.8-bullseye",
14 | // Options
15 | "NODE_VERSION": "none"
16 | }
17 | },
18 |
19 | // Configure tool-specific properties.
20 | "customizations": {
21 | // Configure properties specific to VS Code.
22 | "vscode": {
23 | // Set *default* container specific settings.json values on container create.
24 | "settings": {
25 | "python.defaultInterpreterPath": "/usr/local/bin/python",
26 | "python.linting.enabled": true,
27 | "python.linting.pylintEnabled": true,
28 | "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
29 | "python.formatting.blackPath": "/usr/local/py-utils/bin/black",
30 | "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
31 | "python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
32 | "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
33 | "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
34 | "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
35 | "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
36 | "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint"
37 | },
38 |
39 | // Add the IDs of extensions you want installed when the container is created.
40 | "extensions": [
41 | "ms-python.python",
42 | "ms-python.vscode-pylance",
43 | "GitHub.copilot"
44 | ]
45 | }
46 | },
47 |
48 | // Use 'forwardPorts' to make a list of ports inside the container available locally.
49 | // "forwardPorts": [],
50 |
51 | // Use 'postCreateCommand' to run commands after the container is created.
52 | "postCreateCommand": "pip3 install --user -r requirements.txt",
53 |
54 | // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
55 | "remoteUser": "vscode",
56 | "features": {
57 | "azure-cli": "latest"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/.github/workflows/docker_image.yml:
--------------------------------------------------------------------------------
1 | name: Docker Image CI
2 |
3 | on:
4 | # Using pattern as described in https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#using-filters
5 | pull_request:
6 | branches:
7 | - main
8 | push:
9 | branches:
10 | - main
11 | # Allow mannually trigger
12 | workflow_dispatch:
13 |
14 | jobs:
15 |
16 | build:
17 |
18 | runs-on: ubuntu-latest
19 |
20 | steps:
21 | - uses: actions/checkout@v3
22 | - run: echo "Job being triggered by ${{ github.event_name }} event"
23 | - name: Build the Codespaces container image
24 | run: docker build . --file .devcontainer/Dockerfile
25 | - run: echo "Job status is ${{ job.status }}."
26 |
--------------------------------------------------------------------------------
/.github/workflows/web_app.yml:
--------------------------------------------------------------------------------
1 | name: Build and deploy Python app to Azure Web App
2 |
3 | env:
4 | AZURE_WEBAPP_NAME: your-app-name # set this to your application's name
5 | WORKING_DIRECTORY: '.' # set this to the path to your path of working directory inside github repository, defaults to the repository root
6 | PYTHON_VERSION: '3.9'
7 | STARTUP_COMMAND: 'gunicorn -w 2 -k uvicorn.workers.UvicornWorker webapp.main:app' # set this to the startup command required to start the gunicorn server. default it is empty
8 |
9 | on:
10 | # uncomment the next two lines to deploy on every push to main
11 | #push:
12 | # branches: [ "main" ]
13 | workflow_dispatch:
14 |
15 | permissions:
16 | contents: read
17 |
18 | jobs:
19 | build-and-deploy:
20 | runs-on: ubuntu-latest
21 | environment: dev
22 | steps:
23 | # checkout the repo
24 | - uses: actions/checkout@master
25 | # setup python
26 | - name: Setup Python
27 | uses: actions/setup-python@v1
28 | with:
29 | python-version: ${{ env.PYTHON_VERSION }}
30 | # install dependencies
31 | - name: python install
32 | working-directory: ${{ env.WORKING_DIRECTORY }}
33 | run: |
34 | sudo apt install python${{ env.PYTHON_VERSION }}-venv
35 | python -m venv --copies antenv
36 | source antenv/bin/activate
37 | pip install setuptools
38 | pip install -r requirements.txt
39 | # Azure login
40 | - uses: azure/login@v1
41 | with:
42 | creds: ${{ secrets.AZURE_CREDENTIALS }}
43 | - uses: azure/appservice-settings@v1
44 | with:
45 | app-name: ${{ env.AZURE_WEBAPP_NAME }}
46 | mask-inputs: false
47 | general-settings-json: '{"linuxFxVersion": "PYTHON|${{ env.PYTHON_VERSION }}"}' #'General configuration settings as Key Value pairs'
48 | # deploy web app
49 | - uses: azure/webapps-deploy@v2
50 | with:
51 | app-name: ${{ env.AZURE_WEBAPP_NAME }}
52 | package: ${{ env.WORKING_DIRECTORY }}
53 | startup-command: ${{ env.STARTUP_COMMAND }}
54 | # Azure logout
55 | - name: logout
56 | run: |
57 | az logout
58 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Microsoft Open Source Code of Conduct
2 |
3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
4 |
5 | Resources:
6 |
7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Microsoft Corporation.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=526682400)
2 |
3 | # Python HTTP API with GitHub Codespaces and Copilot
4 |
5 | _Run a Python API in this ready-to-use-repository in minutes_
6 |
7 | By opening this template respository in Codespaces, you can quickly get hands-on with a Python web app that serves an HTTP API. You'll get to focus on working with the project instead of setup and configuration. And then you'll make changes to the code using [GitHub Copilot](https://copilot.github.com/), a new AI-powered code completion tool that helps you write code faster.
8 |
9 | ## 🚀 Quick start
10 | 1. [Follow the steps](#--try-it-out) to setup your Codespace and run the app.
11 | 1. [Make changes to the application](#make-changes-using-copilot) using [GitHub Copilot](https://copilot.github.com/) to make changes to the code.
12 | 1. Take the challenge and deploy your app to Azure.
13 |
14 | 🤔 Curious? Watch the following video where we explain all the details:
15 |
16 | [](https://youtu.be/_i9Pywj3rSg "Python Development environment with Codespaces")
17 |
18 |
19 | Learn more about APIs
21 |
22 | An API (Application Programming Interface) describes a way for two computers to interact.
23 | An HTTP API allows an Internet-connected computer to send an HTTP request to another Internet-connected computer
24 | and receive a response. For example, my computer could send a request to
25 | `http://a-weather-website-api.com/api/city=Los+Angeles` and receive back data like `{"high": 72, "low": 66}`.
26 |
27 | HTTP APIs often provide either data or functionality that's unique to a service, like the example API for the weather website. A weather website could provide additional API endpoints for other weather-related functionality, like upcoming forecasts or historical data. Any website can decide to offer an API if it thinks it has helpful functionality to share
28 | with other computers. In this project, you'll run an HTTP API that generates a random token.
29 | 🎥 Watch the video tutorial to learn more about Codespaces
35 |
36 | [](https://aka.ms/CodespacesVideoTutorial "Codespaces Tutorial")
37 | Run FastAPI inside the Codespace
77 |
78 | The API included in this template repository has a single endpoint that generates a token. Get it up and running using the following steps:
79 |
80 | 1. Open up a terminal window by opening up the command palette (Ctrl-Shift-P or Cmd-Shift-P) and then select "Open new Terminal" command.
81 | 1. Run `uvicorn` in the console to start up your API application:
82 |
83 | ```console
84 | uvicorn --host 0.0.0.0 webapp.main:app --reload
85 | ```
86 |
87 | You should see output similar to:
88 |
89 | ```output
90 | INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
91 | INFO: Started reloader process [28720]
92 | INFO: Started server process [28722]
93 | INFO: Waiting for application startup.
94 | INFO: Application startup complete.
95 | ```
96 |
97 | You'll get a pop-up that says your application is available at port 8000. Click the button to open it in the browser.
98 | 1. Once the site loads, click on the _Try it Out_ button or append `/docs` to the URL in the address bar. The automatically generated API documentation should load and look like this:
99 |
100 | 
101 |
102 | 1. Finally, try to interact with the API by sending a request using the self-documented page. Click on the _POST_ button and then on the _Try it Out_ button:
103 |
104 | 
105 |
106 | 🔒 Do you see the lock next to the URL of the website in the browser? That indicates the website is being served over a secure HTTPS connection which encrypts the HTTP responses. That's very important whenever an API can receive sensitive data or respond with sensitive data (like a password).
107 |
108 | Use Copilot
200 |
201 | ### Step 1: Change the HTML to make it interactive
202 | Open the [index.html file](./webapp/static/index.html) and delete the following line:
203 |
204 | ```html
205 |
206 | ```
207 |
208 | Now add a comment so that Copilot can generate code for you:
209 |
210 | ```html
211 |
212 | ```
213 | This should be enough for Copilot to generate code for you after you press `Enter` (or `Return`).
214 | If not, use `Ctrl+Enter` to give multiple suggestions, choose one that fits better to the code below.
215 | Remember, it is possible that copilot will not generate the exact snippet! In this case, type or overwrite the suggestion for the code below.
216 | The generated code should look like this:
217 |
218 | ```html
219 |
223 |
224 |
242 | ```
243 |
244 | Run the application and verify the form shows up.
245 |
246 | ### Step 2: Update the HTML to fix a bug
247 | The generated code introduced a couple of issues. First, the button is not working. Second, the form is not using the right JSON key when submitting the text to the API endpoint. Let's fix that.
248 |
249 | Change the body of the request to use the length key instead of text:
250 |
251 | ```javascript
252 | body: JSON.stringify({ length: input })
253 | ```
254 |
255 | Now, lets change the `innerHTML` to use the `token` key instead of `result`:
256 |
257 | ```javascript
258 | result.innerHTML = data.token;
259 | ```
260 |
261 | Run the application and verify the form is now working.
262 |
263 | ### Step 3: Change the form to use a dropdown
264 | The form is currently accepting any text as input. Let's change it to use a dropdown instead. Add a comment so that Copilot can generate code for you. Delete the following line:
265 |
266 | ```html
267 |
268 | ```
269 |
270 | And add the following comment so that Copilot can generate code for you:
271 |
272 | ```html
273 |
274 | ```
275 |
276 | The generated code should now look like this:
277 |
278 | ```html
279 |
285 | ```
286 |
287 | Run the application again to verify the dropdown is working.
288 |
289 | ### Step 4: Add a new API endpoint
290 | Now let's add some new functionality to the API. Add a new endpoint to the API that accepts a text and returns a list of tokens. Add the following comment so that Copilot can generate a Pydantic model for you:
291 |
292 | ```python
293 | # Create a Pydantic model that accepts a JSON body with a single field called "text", which is a string
294 | ```
295 |
296 | The generated model should look like this:
297 |
298 | ```python
299 | class Text(BaseModel):
300 | text: str
301 | ```
302 |
303 | Next, add the following comment so that Copilot can add a new endpoint:
304 |
305 | ```python
306 | # Create a FastAPI endpoint that accepts a POST request with a JSON body containing a single field called "text" and returns a checksum of the text
307 | ```
308 |
309 | The generated code should look like this:
310 |
311 | ```python
312 | @app.post('/checksum')
313 | def checksum(body: Text):
314 | """
315 | Generate a checksum of the text. Example POST request body:
316 |
317 | {
318 | "text": "Hello World!"
319 | }
320 | """
321 | checksum = base64.b64encode(os.urandom(64))[:20].decode('utf-8')
322 | return {'checksum': checksum}
323 | ```
324 |
325 | The generated code will cause the application to crash. This is because the `base64` and `os` modules are not imported. Add the following lines to the top of the file:
326 |
327 | ```python
328 | import base64
329 | ```
330 |
331 | Finally, verify the new endpoint is working by going to the `/docs` page and trying out the new endpoint.
332 |
333 | Congratulations! You've used Copilot to not only generate code, but also do it in a way that is interactive and fun. You can now use Copilot to generate code for you in any of your projects, including writing documentation, generating models, and more! Even portions of this README were generated using Copilot suggestions 🧐
334 |
335 | Create an Azure App Service
351 |
352 | Now, you are going to set up automatic deployment of the application using Azure plus GitHub actions! However, you first need to configure some Azure services.
353 |
354 | 1. Open the [Azure Cloud Shell](https://shell.azure.com/?WT.mc_id=academic-77460-alfredodeza).
355 | 1. Use the Bash shell (not PowerShell!) for these steps.
356 | 1. If it says "You have no storage mounted", select a subscription in your account and click "Create storage". The Cloud Shell uses that storage resource to store data generated during your shell sessions.
357 | 1. Create a *Resource Group* which will group together the different Azure resources used for the app:
358 | ```
359 | az group create --name demo-fastapi --location "East US"
360 | ```
361 | 1. You'll see a JSON response with details about the newly created resource, for this command and all the commands that follow.
362 | 1. Create the **FREE** *App Service Plan*:
363 | ```
364 | az appservice plan create --name "demo-fastapi" --resource-group demo-fastapi --is-linux --sku FREE
365 | ```
366 | 1. Create a random identifier for a unique webapp name:
367 | ```
368 | let "randomIdentifier=$RANDOM*$RANDOM"
369 | ```
370 | 1. Create the *Web App Service* with a placeholder container using the `randomIdentifier` variable from before:
371 | ```
372 | az webapp create --name "demo-fastapi-$randomIdentifier" --resource-group demo-fastapi --plan demo-fastapi --runtime "PYTHON:3.9"
373 | ```
374 | 1. Head to the Azure portal [App Services list](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.Web%2Fsites) and confirm that your newly created service is listed.
375 |
376 | Create an Azure Service Principal
381 |
382 | Next, create an Azure Service Principal, which is a special type of account that has permissions necessary to authenticate from GitHub to Azure:
383 |
384 | 1. Find the ID of your Azure Subscription [in the Azure portal](https://portal.azure.com/#view/Microsoft_Azure_Billing/SubscriptionsBlade?WT.mc_id=academic-77460-alfredodeza) or [by following this guide](https://learn.microsoft.com/azure/azure-portal/get-subscription-tenant-id?WT.mc_id=academic-77460-alfredodeza).
385 | 1. Create a Service Principal with a "contributor" role that is allowed to make changes to any resources in that subscription. Replace $AZURE_SUBSCRIPTION_ID with the ID you found in step 1 and run this command:
386 |
387 | ```
388 | az ad sp create-for-rbac --sdk-auth --name "github-deployer" --role contributor --scopes /subscriptions/$AZURE_SUBSCRIPTION_ID
389 | ```
390 |
391 | 1. Capture the output and add it as a [Github repository secret](/../../settings/secrets/actions/new) with the name `AZURE_CREDENTIALS`. (_If that link doesn't work, make sure you're reading this on your own copy of the repo, not the original template._)
392 |
393 | Setup GitHub Actions
398 |
399 | Now that you have all the Azure resources created, you need to update the GitHub Action workflow file with the name of your webapp.
400 |
401 | 1. Find your app name. It should look something like `demo-fastapi-97709018` but with a different random number at the end,
402 | and you can find it in the Azure portal or the Cloud Shell commands.
403 | 2. Open the [.github/workflows/web_app.yml](/../../edit/main/.github/workflows/web_app.yml) file and update the value of `AZURE_WEBAPP_NAME` to your app name.
404 | 3. Commit and push the changes to the Github repository:
405 |
406 | ```
407 | git add .github/workflows/web_app.yml
408 | git commit -m "Updating workflow file"
409 | git push
410 | ```
411 |
412 | 🏃 Deploy your app!
416 |
417 | Before continuing, check the following:
418 |
419 | 1. You've created an Azure Service Principal and saved it as a [repository secret](/../../settings/secrets/) as `AZURE_CREDENTIALS`.
420 | 1. You've created an [App Service](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.Web%2Fsites) with a valid name and the site is already available with the default static content.
421 |
422 | To deploy:
423 |
424 | 1. Navigate to [repository actions](/../../actions/workflows/web_app.yml). (_If that link doesn't open the "Build and deploy Python app" workflow, make sure you're reading this on your own copy of the repo._)
425 | 3. Select _Run workflow_ and select the green button inside the pop-up to run the workflow.
426 |
427 | **Deploying can take a couple of minutes**. Make sure you stream the logs in the Azure Cloud Shell to check the progress:
428 |
429 | ```
430 | az webapp log tail --name $AZURE_WEBAPP_NAME --resource-group $AZURE_RESOURCE_GROUP
431 | ```
432 |
433 | 4. Once deployment is complete, visit your website at a URL like `http://demo-fastapi-97709018.azurewebsites.net/`,
434 | where the random number is your unique random number. You can find the website URL in the Azure portal or in the deployment logs if you forgot the number.
435 | 5. 🎉 Celebrate a successful deployment! You now have a URL that you can share with classmates, friends, and family.
436 |
437 | ### Destroy resources when complete
438 |
439 | You likely don't want to keep this particular website running forever in the cloud, so you should cleanup your Azure resources by destroying the resource group. You can do it in the Azure Cloud Shell by referencing the group name you created initially (`demo-fastapi` in the examples):
440 |
441 | ```
442 | az group delete --name demo-fastapi
443 | ```
444 |
445 | ### Deployment Troubleshooting
446 |
447 | When deploying, you might encounter errors or problems, either on the automation part of it (GitHub Actions) or on the deployment destination (Azure Web Apps).
448 |
449 | You can check the logs of the Github Actions workflow by selecting the latest workflow from the _Actions_ tab. Find the first step that has a broken icon next to it, and expand that step to see what went wrong in it.
450 |
451 | If running into trouble with the Azure deployment, check logs in the portal or use the following with the Azure CLI:
452 |
453 | ```
454 | az webapp log tail --name $AZURE_WEBAPP_NAME --resource-group $AZURE_RESOURCE_GROUP
455 | ```
456 |
457 | Update both variables to match your environment.
458 |
459 |
460 | Aprende más sobre las APIs
17 |
18 | Una API (Interfaz de programación de aplicaciones) describe una forma en que dos equipos interactúan.
19 | Una API HTTP permite que un equipo conectado a Internet envíe una solicitud HTTP a otro equipo conectado a Internet y recibirá una respuesta. Por ejemplo, mi equipo podría enviar una solicitud a `http://a-weather-website-api.com/api/city=Los+Angeles` y recibirá datos como `{"high": 72, "low": 66}`.
20 |
21 | Las API HTTP a menudo proporcionan datos o funcionalidad que es exclusiva de un servicio, como la API de ejemplo para el sitio web meteorológico. Un sitio web meteorológico podría proporcionar endpoints de API adicionales para otras funciones relacionadas con el clima, como próximos pronósticos o datos históricos. Cualquier sitio web puede decidir ofrecer una API si cree que tiene una funcionalidad útil para compartir con otras computadoras. **En este proyecto, ejecutarás una API HTTP que genera un token aleatorio.**
22 |
23 |
24 | 🎥 Ve este video tutorial para obtener más información sobre Codespaces
30 |
31 | [](https://aka.ms/CodespacesVideoTutorial "Codespaces Tutorial")
32 | Ejecuta FastAPI dentro del Codespace
71 |
72 | La API incluida en este repositorio de plantillas tiene un único extremo que genera un token. Pon lo en marcha siguiendo estos pasos:
73 |
74 | 1. Abre una terminal utilizando estos comandos (Ctrl-Shift-P o Cmd-Shift-P) y luego selecciona el comando "Abrir nueva terminal".
75 | 1. Ejecuta `uvicorn` en la consola para iniciar la aplicación de API:
76 |
77 | ```console
78 | uvicorn --host 0.0.0.0 webapp.main:app --reload
79 | ```
80 |
81 | Deberías ver una salida similar a:
82 |
83 | ```output
84 | INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
85 | INFO: Started reloader process [28720]
86 | INFO: Started server process [28722]
87 | INFO: Waiting for application startup.
88 | INFO: Application startup complete.
89 | ```
90 |
91 | Aparecerá una ventana que dice que tu aplicación está disponible en el puerto 8000. Haz clic en el botón para abrirlo en el navegador.
92 |
93 | 1. Una vez que se cargue el sitio, haz clic en el botón _Try it Out_ o agregue `/docs` a la URL en la barra de direcciones. La documentación autogenerada de la API debería cargarse y tener este aspecto:
94 |
95 | 
96 |
97 | 1. Finalmente, intenta interactuar con la API enviando una solicitud utilizando la página autodocumentada. Haz clic en el botón _POST_ y luego en el botón _Try it Out_:
98 |
99 | 
100 |
101 | 🔒 ¿Ves el candado junto a la URL del sitio web en el navegador? Esto indica que el sitio web esta interactuando a través de una conexión HTTPS segura que cifra las respuestas HTTP. Es muy importante siempre que una API pueda recibir datos confidenciales o responder con datos confidenciales (como una contraseña).
102 |
103 | Crea un Azure App Service
191 |
192 | ¡Ahora, vas a configurar la implementación automática de la aplicación usando Azure más GitHub Actions! Sin embargo, primero debes configurar algunos servicios de Azure.
193 |
194 | 1. Abre [Azure Cloud Shell](https://shell.azure.com/?WT.mc_id=academic-77460-alfredodeza).
195 | 1. Usa el Bash Shell (¡no PowerShell!) para estos pasos.
196 | 1. Si dice "You have no storage mounted", seleccione una suscripción en su cuenta y haga clic en "Create storage". Cloud Shell utiliza ese recurso de almacenamiento para almacenar los datos generados durante las sesiones de shell.
197 |
198 | 1. Crea un *Resource Group* que agrupe los diferentes recursos de Azure usados para la aplicación:
199 |
200 | ```
201 | az group create --name demo-fastapi --location "East US"
202 | ```
203 | 1. Veras una respuesta JSON con detalles sobre el recurso recién creado, para este comando y todos los comandos que siguen.
204 | 1. Crea el *App Service Plan* **GRATIS**:
205 | ```
206 | az appservice plan create --name "demo-fastapi" --resource-group demo-fastapi --is-linux --sku FREE
207 | ```
208 | 1. Crea un identificador aleatorio para un nombre de aplicación web único:
209 | ```
210 | let "randomIdentifier=$RANDOM*$RANDOM"
211 | ```
212 | 1. Crea el *Web App Service* con un contenedor placeholder utilizando la variable 'randomIdentifier' de antes:
213 | ```
214 | az webapp create --name "demo-fastapi-$randomIdentifier" --resource-group demo-fastapi --plan demo-fastapi --runtime "PYTHON:3.9"
215 | ```
216 | 1. Dirígete al Portal de Azure [App Services list](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.Web%2Fsites) y confirma que el servicio recién creado aparece en la lista.
217 | Creación de un Azure Service Principal
222 |
223 | A continuación, crea un Azure Service Principal, este es un tipo especial de cuenta que tiene los permisos necesarios para autenticarse desde GitHub a Azure:
224 |
225 | 1. Busca el ID de tu suscripción de Azure [en el Portal de Azure](https://portal.azure.com/#view/Microsoft_Azure_Billing/SubscriptionsBlade?WT.mc_id=academic-77460-alfredodeza) o [siguiendo esta guía](https://learn.microsoft.com/azure/azure-portal/get-subscription-tenant-id?WT.mc_id=academic-77460-alfredodeza).
226 | 1. Crea un Service Principal con un rol de "contributor" que permita realizar cambios en cualquier recurso de esa suscripción. Reemplace $AZURE_SUBSCRIPTION_ID por el ID que encontraste en el paso 1 y ejecuta este comando:
227 |
228 |
229 | ```
230 | az ad sp create-for-rbac --name "CICD" --role contributor --scopes /subscriptions/$AZURE_SUBSCRIPTION_ID --sdk-auth
231 | ```
232 |
233 | 1. Captura la salida y agrégala como un [Secreto del repositorio de Github](/../../settings/secrets/actions/new) con el nombre `AZURE_CREDENTIALS`.
234 |
235 | Configura GitHub Actions
240 |
241 | Ahora que ha creado todos los recursos de Azure, debes actualizar el archivo del workflow de GitHub Action con el nombre de su aplicación web.
242 |
243 | 1. Busca el nombre de tu aplicación. Debería tener un aspecto similar a `demo-fastapi-97709018` pero con un número aleatorio diferente al final, y puedes encontrarlo en Azure Portal o con los comandos de Cloud Shell.
244 | 2. Abre el archivo [.github/workflows/web_app.yml](/.. /.. /edit/main/.github/workflows/web_app.yml) y actualiza el valor de `AZURE_WEBAPP_NAME` al nombre de tu aplicación.
245 |
246 | 🏃 ¡Sube tu app!
250 |
251 | Antes de continuar, verifica lo siguiente:
252 |
253 | 1. Has creado un Azure Service Principal y la has guardado un [repositorio secreto](/../../settings/secrets/) como `AZURE_CREDENTIALS`.
254 | 1. Has creado un [App Service](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.Web%2Fsites) con un nombre válido y el sitio ya está disponible con el contenido estático predeterminado.
255 |
256 | Para implementar:
257 |
258 | 1. Ve a [acciones del repositorio](/../../actions/workflows/web_app.yml)y haz clic en _Run workflow_ y luego en el botón verde para ejecutarlo.
259 |
260 | **La implementación puede tardar un par de minutos**. Asegúrate de ver los registros en Azure Cloud Shell para comprobar el progreso:
261 |
262 | ```
263 | az webapp log tail --name $AZURE_WEBAPP_NAME --resource-group $AZURE_RESOURCE_GROUP
264 | ```
265 |
266 | ### Eliminando recursos cuando se complete
267 |
268 | Después de la implementación, asegúrate de limpiar los recursos eliminando el grupo de recursos. Puedes hacerlo en Azure Cloud Shell haciendo referencia al nombre del grupo que creó inicialmente (`demo-fastapi` en los ejemplos):
269 |
270 | ```
271 | az group delete --name demo-fastapi
272 | ```
273 |
274 | ### Solución de problemas de implementación
275 |
276 | Al implementar, es posible que encuentres errores o problemas, ya sea en la parte de automatización (GitHub Actions) o en el momento de implementarlo (Azure Web Apps).
277 |
278 | Si tienes problemas, comprueba los registros en el portal o usa lo siguiente con la CLI de Azure:
279 |
280 | ```
281 | az webapp log tail --name $AZURE_WEBAPP_NAME --resource-group $AZURE_RESOURCE_GROUP
282 | ```
283 |
284 | Actualice ambas variables para que coincidan con tu entorno.
285 |
286 | Saiba mais sobre APIs
21 |
22 | Uma API (Interface de Programação de Aplicativos) descreve uma maneira para dois computadores interagirem. Uma API HTTP permite que um computador conectado à Internet envie uma solicitação HTTP para outro computador conectado à Internet e receba uma resposta. Por exemplo, meu computador pode enviar uma solicitação para `http://um-site-de-previsao-do-tempo.com/api/cidade=Los+Angeles` e receber dados de volta, como `{"alta": 72, "baixa": 66}`.
23 |
24 | APIs HTTP frequentemente fornecem dados ou funcionalidades exclusivas de um serviço, como o exemplo da API do site de previsão do tempo. Um site de previsão do tempo pode fornecer endpoints de API adicionais para outras funcionalidades relacionadas ao clima, como previsões futuras ou dados históricos. Qualquer site pode optar por oferecer uma API se acreditar que possui funcionalidades úteis para compartilhar com outros computadores. Neste projeto, você executará uma API HTTP que gera um token aleatório.
25 | 🎥 Assista ao tutorial em vídeo para aprender mais sobre Codespaces
31 |
32 | [](https://aka.ms/CodespacesVideoTutorial "Tutorial do Codespaces")
33 | O que é um ponto de extremidade?
72 |
73 | Um ponto de extremidade é uma URL estável e durável que representa um recurso específico em uma API. Ele fornece uma maneira de interagir com esse recurso, enviar solicitações e receber respostas. Em termos simples, um ponto de extremidade é um "ponto de entrada" para uma API.
74 |
75 | Características de um ponto de extremidade:
76 |
77 | - **URL estável e durável**: Um ponto de extremidade é acessado por meio de uma URL específica, que permanece consistente ao longo do tempo. Por exemplo, uma URL estável e durável (como endpoint-name.region.inference.ml.azure.com).
78 |
79 | - **Mecanismo de autenticação e autorização**: Para garantir a segurança e controlar o acesso ao recurso, os pontos de extremidade podem exigir autenticação e autorização. Isso pode envolver o uso de tokens, chaves de API ou outros métodos de autenticação.
80 |
81 | - **Implantação e roteamento**: Um ponto de extremidade pode ter várias implantações, que são responsáveis por executar a lógica do recurso e fornecer as respostas adequadas. Essas implantações podem estar localizadas em servidores diferentes, dependendo dos requisitos de recursos e escalabilidade. O mecanismo de roteamento direciona as solicitações recebidas para as implantações corretas.
82 |
83 | Portanto, um ponto de extremidade é um componente fundamental em uma API. Ele representa um recurso específico e define a maneira como os clientes podem interagir com ele, fornecendo uma URL estável, um mecanismo de autenticação e autorização, e encaminhando as solicitações para as implantações corretas.
84 |
85 | Executando o FastAPI dentro do Codespace
88 |
89 | A API incluída neste modelo de repositório possui um único ponto de extremidade (endpoint) que gera um token. Coloque-a em funcionamento seguindo as etapas a seguir:
90 |
91 | 1. Abra um terminal, abrindo o painel de comandos (Ctrl-Shift-P ou Cmd-Shift-P) e selecione o comando "Abrir novo terminal".
92 | 2. Execute `uvicorn` no console para iniciar o aplicativo da API:
93 |
94 | ```console
95 | uvicorn --host 0.0.0.0 webapp.main:app --reload
96 | ```
97 |
98 | Você verá uma saída semelhante a esta:
99 |
100 | ```output
101 | INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
102 | INFO: Started reloader process [28720]
103 | INFO: Started server process [28722]
104 | INFO: Waiting for application startup.
105 | INFO: Application startup complete.
106 | ```
107 |
108 | Você verá uma janela pop-up informando que o seu aplicativo está disponível na porta 8000. Clique no botão para abri-lo no navegador.
109 | 3. Assim que o site for carregado, clique no botão _Try it Out_ ou adicione `/docs` à URL na barra de endereços. A documentação da API gerada automaticamente deve ser carregada e parecer assim:
110 |
111 | 
112 |
113 |
114 | 4. Por fim, tente interagir com a API enviando uma solicitação usando a página auto documentada. Clique no botão _POST_ e depois no botão _Try it Out_:
115 |
116 | 
117 |
118 |
119 | 🔒 Você vê o cadeado ao lado da URL do site no navegador? Isso indica que o site está sendo servido por meio de uma conexão HTTPS segura, que criptografa as respostas HTTP. Isso é muito importante sempre que uma API pode receber dados sensíveis ou responder com dados sensíveis (como uma senha).
120 |
121 | Utilizando o Copilot
213 |
214 | ### Passo 1: Alterar o HTML para torná-lo interativo
215 |
216 | Abra o arquivo [index.html](./webapp/static/index.html) e exclua a seguinte linha:
217 |
218 | ```html
219 |
220 | ```
221 |
222 | Agora, adicione um comentário para que o Copilot possa gerar código para você:
223 |
224 | ```html
225 |
226 | ```
227 |
228 | Isso deve ser suficiente para o Copilot gerar código para você depois de pressionar `Enter` (ou `Return`).
229 | Se não for o caso, use `Ctrl+Enter` para obter várias sugestões e escolha aquela que se encaixa melhor no código abaixo.
230 | Lembre-se de que é possível que o Copilot não gere o trecho exato! Nesse caso, digite ou substitua a sugestão para o código abaixo.
231 | O código gerado deve ser semelhante a este:
232 |
233 | ```html
234 |
238 |
239 |
259 | ```
260 |
261 | Execute a aplicação e verifique se o formulário aparece.
262 |
263 | ### Passo 2: Atualizar o HTML para corrigir um bug
264 |
265 | O código gerado introduziu alguns problemas. Primeiro, o botão não está funcionando. Segundo, o formulário não está usando a chave JSON correta ao enviar o texto para o endpoint da API. Vamos corrigir isso.
266 |
267 | Altere o corpo da solicitação para usar a chave `length` em vez de `text`:
268 |
269 | ```javascript
270 | body: JSON.stringify({ length: input })
271 | ```
272 |
273 | Agora, vamos alterar o `innerHTML` para usar a chave `token` em vez de `result`:
274 |
275 | ```javascript
276 | result.innerHTML = data.token;
277 | ```
278 |
279 | Execute a aplicação e verifique se o formulário está funcionando agora.
280 |
281 | ### Passo 3: Alterar o formulário para usar um menu suspenso
282 |
283 | Atualmente, o formulário aceita qualquer texto como entrada. Vamos alterá-lo para usar um menu suspenso. Adicione um comentário para que o Copilot possa gerar código para você. Exclua a seguinte linha:
284 |
285 | ```html
286 |
287 | ```
288 |
289 | E adicione o seguinte comentário para que o Copilot possa gerar código para você:
290 |
291 | ```html
292 |
293 | ```
294 |
295 | O código gerado agora deve ficar assim:
296 |
297 | ```html
298 |
304 | ```
305 |
306 | Execute a aplicação novamente para verificar se o menu suspenso está funcionando corretamente.
307 |
308 | ### Passo 4: Adicionar um novo ponto de extremidade (endpoint) à API
309 |
310 | Agora vamos adicionar uma nova funcionalidade à API. Adicione um novo ponto de extremidade (endpoint) à API que aceite um texto e retorne uma lista de tokens. Adicione o seguinte comentário para que o Copilot possa gerar um modelo Pydantic para você:
311 |
312 | ```python
313 | # Crie um modelo Pydantic que aceita um corpo JSON com um único campo chamado "text", que é uma string
314 | ```
315 |
316 | O modelo gerado deve ficar assim:
317 |
318 | ```python
319 | class Text(BaseModel):
320 | text: str
321 | ```
322 |
323 | Em seguida, adicione o seguinte comentário para que o Copilot possa adicionar um novo endpoint:
324 |
325 | ```python
326 | # Crie um endpoint FastAPI que aceita uma solicitação POST com um corpo JSON contendo um único campo chamado "text" e retorna um checksum do texto
327 | ```
328 |
329 | O código gerado deve ficar assim:
330 |
331 | ```python
332 | @app.post('/checksum')
333 | def checksum(body: Text):
334 | """
335 | Gere um checksum do texto. Exemplo de corpo de solicitação POST:
336 |
337 | {
338 | "text": "Olá mundo!"
339 | }
340 | """
341 | checksum = base64.b64encode(os.urandom(64))[:20].decode('utf-8')
342 | return {'checksum': checksum}
343 | ```
344 |
345 | O código gerado fará com que a aplicação falhe. Isso ocorre porque os módulos `base64` e `os` não estão sendo importados. Adicione as seguintes linhas no início do arquivo:
346 |
347 | ```python
348 | import base64
349 | import os
350 | ```
351 |
352 | Por fim, verifique se o novo ponto de extremidade (endpoint) está funcionando acessando a página `/docs` e testando o novo endpoint.
353 |
354 | Parabéns! Você usou o Copilot não apenas para gerar código, mas também para fazer isso de forma interativa e divertida. Agora você pode usar o Copilot para gerar código em qualquer um de seus projetos, incluindo escrever documentação, gerar modelos e muito mais! Até mesmo partes deste README foram geradas usando sugestões do Copilot 🧐
355 |
356 | Criar um Serviço de Aplicativo do Azure
372 |
373 | Agora você irá configurar a publicação automática da aplicação usando o Azure e o GitHub Actions! No entanto, primeiro você precisa configurar alguns serviços do Azure.
374 |
375 | 1. Abra o [Azure Cloud Shell](https://shell.azure.com/?WT.mc_id=academic-77460-alfredodeza).
376 | 2. Use o Bash Shell (não o PowerShell!) para executar estas etapas.
377 | 3. Se aparecer a mensagem "You have no storage mounted", selecione uma assinatura em sua conta e clique em "Create storage". O Cloud Shell usará esse recurso de armazenamento para armazenar os dados gerados durante suas sessões no shell.
378 | 4. Crie um *Grupo de Recursos* que agrupará os diferentes recursos do Azure usados pela aplicação:
379 | ```
380 | az group create --name demo-fastapi --location "East US"
381 | ```
382 | 5. Você verá uma resposta em JSON com detalhes sobre o novo recurso criado, para este comando e todos os comandos que seguem.
383 | 6. Crie o *Plano de Serviço de Aplicativo* **GRATUITO**:
384 | ```
385 | az appservice plan create --name "demo-fastapi" --resource-group demo-fastapi --is-linux --sku FREE
386 | ```
387 | 7. Crie um identificador aleatório para um nome exclusivo do web app:
388 | ```
389 | let "randomIdentifier=$RANDOM*$RANDOM"
390 | ```
391 | 8. Crie o *Serviço de Aplicativo da Web* com um contêiner reservado usando a variável `randomIdentifier` criada anteriormente:
392 | ```
393 | az webapp create --name "demo-fastapi-$randomIdentifier" --resource-group demo-fastapi --plan demo-fastapi --runtime "PYTHON:3.9"
394 | ```
395 | 9. Acesse a lista de [Serviços de Aplicativos](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.Web%2Fsites) no portal do Azure e verifique se o serviço recém-criado está listado.
396 |
397 | Criando um Azure Service Principal
402 |
403 | A seguir, crie um Azure Service Principal, que é um tipo especial de conta que possui as permissões necessárias para autenticação do GitHub no Azure:
404 |
405 | 1. Encontre o ID de sua assinatura do Azure
406 |
407 | [no portal do Azure](https://portal.azure.com/#view/Microsoft_Azure_Billing/SubscriptionsBlade?WT.mc_id=academic-77460-alfredodeza) ou [seguindo este guia](https://learn.microsoft.com/azure/azure-portal/get-subscription-tenant-id?WT.mc_id=academic-77460-alfredodeza).
408 | 2. Crie um Azure Service Principal com a função "contributor" que está autorizada a fazer alterações em todos os recursos dessa assinatura. Substitua $AZURE_SUBSCRIPTION_ID pelo ID encontrado no passo 1 e execute o seguinte comando:
409 |
410 | ```
411 | az ad sp create-for-rbac --sdk-auth --name "github-deployer" --role contributor --scopes /subscriptions/$AZURE_SUBSCRIPTION_ID
412 | ```
413 |
414 | 3. Copie a saída e adicione-a como um [segredo do repositório do GitHub](/../../settings/secrets/actions/new) com o nome `AZURE_CREDENTIALS`. (_Se esse link não funcionar, certifique-se de que você está lendo isso em sua própria cópia do repositório, não no modelo original._)
415 |
416 | Configurar o GitHub Actions
421 |
422 | Agora que você criou todos os recursos do Azure, precisa atualizar o arquivo de fluxo de trabalho do GitHub Actions com o nome do seu web app.
423 |
424 | 1. Encontre o nome do seu aplicativo. Deve ser algo como `demo-fastapi-97709018`, mas com um número aleatório diferente no final, e você pode encontrá-lo no portal do Azure ou nos comandos do Cloud Shell.
425 | 2. Abra o arquivo [.github/workflows/web_app.yml](/../../edit/main/.github/workflows/web_app.yml) e atualize o valor de `AZURE_WEBAPP_NAME` com o nome do seu aplicativo.
426 | 3. Faça o commit e envie as alterações para o repositório do GitHub:
427 |
428 | ```
429 | git add .github/workflows/web_app.yml
430 | git commit -m "Atualizando arquivo de fluxo de trabalho"
431 | git push
432 | ```
433 |
434 | 🏃 Implante a sua aplicação!
438 |
439 | Antes de continuar, verifique o seguinte:
440 |
441 | 1. Você criou um Azure Service Principal e o salvou como um [segredo do repositório](/../../settings/secrets/) chamado `AZURE_CREDENTIALS`.
442 | 2. Você criou um [Serviço de Aplicativo](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.Web%2Fsites) com um nome válido e o site já está disponível com o conteúdo estático padrão.
443 |
444 | Para implantar:
445 |
446 | 1. Acesse [Actions do repositório](/../../actions/workflows/web_app.yml). (_Se esse link não abrir o fluxo de trabalho "Build and deploy Python app", certifique-se de que você está lendo isso em sua própria cópia do repositório._)
447 | 2. Selecione _Run workflow_ e clique no botão verde dentro da janela pop-up para executar o fluxo de trabalho.
448 |
449 | **A implantação pode levar alguns minutos**. Certifique-se de transmitir os logs no Azure Cloud Shell para verificar o progresso:
450 |
451 | ```
452 | az webapp log tail --name $AZURE_WEBAPP_NAME --resource-group $AZURE_RESOURCE_GROUP
453 | ```
454 |
455 | 3. Após a conclusão da implantação, acesse seu site em uma URL
456 |
457 | como `http://demo-fastapi-97709018.azurewebsites.net/`, em que o número aleatório é o seu número aleatório exclusivo. Você pode encontrar a URL do site no portal do Azure ou nos logs de implantação, caso tenha esquecido o número.
458 | 4. 🎉 Celebre um implantação bem-sucedida! Agora você tem uma URL que pode compartilhar com colegas, amigos e familiares.
459 |
460 | ### Removendo os recursos quando concluído
461 |
462 | Provavelmente você não deseja manter esse site específico em execução na nuvem para sempre, então você deve limpar seus recursos do Azure excluindo o grupo de recursos. Você pode fazer isso no Azure Cloud Shell referenciando o nome do grupo que você criou inicialmente (`demo-fastapi` nos exemplos):
463 |
464 | ```
465 | az group delete --name demo-fastapi
466 | ```
467 |
468 | ### Solução de problemas de implantação
469 |
470 | Ao fazer a implantação, você pode encontrar erros ou problemas, seja na automação (GitHub Actions) ou no destino de implantação (Azure Web Apps).
471 |
472 | Você pode verificar os logs do fluxo de trabalho do GitHub Actions selecionando o fluxo de trabalho mais recente na guia _Actions_. Localize a primeira etapa que tem um ícone quebrado ao lado e expanda essa etapa para ver o que deu errado.
473 |
474 | Se você tiver problemas com a implantação no Azure, verifique os logs no portal ou use o seguinte comando com o Azure CLI:
475 |
476 | ```
477 | az webapp log tail --name $AZURE_WEBAPP_NAME --resource-group $AZURE_RESOURCE_GROUP
478 | ```
479 |
480 | Atualize ambas as variáveis para corresponder ao seu ambiente.
481 |
482 |
32 | 🚀 33 |
34 | 44 |