├── README.md
├── assets
└── img
│ ├── git
│ ├── actions_get_started.png
│ ├── actions_run_workflow.png
│ ├── actions_workfows.png
│ ├── actions_workfows_2.png
│ ├── as_actions.png
│ ├── as_docker.png
│ ├── btn_new.png
│ ├── checkbox_template.png
│ ├── environment_secrets.png
│ ├── file_navigation_add_file.png
│ ├── file_navigation_tags.png
│ ├── fork.png
│ ├── issue.png
│ ├── issues_new_issue.png
│ ├── new_branch.png
│ ├── new_repo.png
│ ├── new_repo_from_template.png
│ ├── pr.png
│ ├── pr_compare.png
│ ├── pr_workflow.png
│ ├── release.png
│ ├── settings_pages.png
│ ├── tabs_actions.png
│ ├── tabs_issues.png
│ ├── tabs_pr.png
│ ├── tabs_settings.png
│ └── tabs_wiki.png
│ ├── slides
│ ├── astrcat_transparent.png
│ ├── bg1.png
│ ├── bg2.png
│ ├── bg3.png
│ ├── fp2.png
│ └── globe.png
│ └── workshop-monitor-presenter.png
├── docs
├── links.md
├── notes.md
├── polls.md
├── proposal.md
├── requirements.md
└── timing.md
├── slides
└── github_fundamentals.pdf
└── src
├── .github
└── workflows
│ └── cicd.yaml
├── Dockerfile
├── README.md
└── app
├── db.json
├── rengine.py
├── requirements.txt
├── run.py
└── test_app.py
/README.md:
--------------------------------------------------------------------------------
1 | # GitHub Fundamentals
2 |
3 | #### Objectives
4 |
5 | To create a movie recommendation engine as an Open Source Software implemented in a microservice architecture.
6 |
7 | By the end of this workshop, you’ll know:
8 |
9 | - [X] History of OpenSource
10 | - [X] How to create an Open Source Software
11 | - [X] [Markdown markup language](https://guides.github.com/features/mastering-markdown/)
12 | - [X] [Docker](#1-github-repository---github-templates)
13 | - [X] [Github Repository / Templates](#1-github-repository---github-templates)
14 | - [X] [Github Codespaces](https://github.com/features/codespaces)
15 | - [X] [Microservice](#2-microservice)
16 | - [X] [Github Issues](#3-github-issues)
17 | - [X] [Github Releases / Tag](#4-github-releases--tags)
18 | - [X] [Github Actions](#5-github-actions)
19 | - [X] [Pull Requests](#6-github-pull-request)
20 | - [X] [Github Advanced Search](#7-github-advanced-search)
21 | - [X] [Github Pages](#8-github-pages)
22 | - [X] [Github Wikis](#9-github-wikis)
23 |
24 | And you’ll be able to:
25 |
26 | - [X] Create an Open Source Software on GitHub
27 | - [X] Collaborate on Open Source Software by creating GitHub Issues and doing Pull Requests
28 | - [X] Build CI/CD pipelines with GitHub Actions
29 | - [X] Host website with GitHub Pages
30 | - [X] Use GitHub’s advanced search
31 | - [X] User GIT version control
32 | - [X] Build software with Docker
33 | - [X] Share your docker images on hub.docker.com
34 |
35 | #### Requirements
36 |
37 | - [GitHub](https://github.com/) account
38 | - [DockerHub](https://hub.docker.com/) account
39 |
40 | # Warning - No license, no Open Source !!!
41 |
42 | Choosing the right license when creating an open-source project is [crucial](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/licensing-a-repository#choosing-the-right-license).
43 |
44 | > "**without a license**, the default copyright laws apply, meaning that you retain all rights to your source code, and **no one may reproduce, distribute, or create derivative works from your work.**"
45 |
46 | There are many available [open-source licenses](https://opensource.org/licenses). If you are confused about which one to use, visit [choosealicense.com](https://choosealicense.com/licenses/) - a comprehensive guide about open source licenses.
47 |
48 | Additional information about **legal aspects of having no license** can be found [here](https://opensource.stackexchange.com/questions/1720/what-can-i-assume-if-a-publicly-published-project-has-no-license).
49 |
50 | ## 1. GitHub Repository / GitHub Templates
51 |
52 |
53 | Context
54 |
55 | #### What is a GitHub repository?
56 |
57 | A repository is a place where your git **project** and its **files** reside. A typical repository stores source code along with the `.git` folder - a directory that tracks snapshots of changes introduced to your files.
58 |
59 | #### Why use the GitHub repository?
60 |
61 | GitHub repository is:
62 | - A place for documenting your project - **GitHub Wiki**
63 | - A place for automatizing tasks within the software development life cycle - **GitHub Actions**
64 | - A place for organizing and tracking work items - **GitHub Projects**
65 | - A forum for sharing and raising questions - **GitHub Issues**
66 | - A place for security scanning - **GitHub Security**
67 | - And many more ...
68 |
69 | #### What is a GitHub template?
70 |
71 | A GitHub Template is a way of marking your repository as a reusable blueprint. GitHub Template allows to create new repositories that preserve the same structure, branches, and files as the blueprint repository.
72 |
73 |
74 |
75 | Let's create our first GitHub repository. This repository will hold `Hello world!` Flask application and will become our GitHub template for a microservice that we'll build in the next step.
76 |
77 | 1. On the [GitHub](https://github.com/) page click [New](https://github.com/new) button.
78 |
79 | 
80 |
81 | 1. Name the repository `flask-init-mini` and click the `Create repository` button.
82 |
83 | 
84 |
85 | 1. What's left is to create a Flask application. If you are not familiar with Flask, read the [quick start](https://flask.palletsprojects.com/en/1.1.x/quickstart/) guide. In GitHub's interface, click the `Add file` button and select `Create new file`.
86 |
87 | [](https://github.com/ldynia/flask-init-mini)
88 |
89 | **app/requirements.txt**
90 |
91 | ```
92 | coverage==7.3.2
93 | Flask==3.0.0
94 | pytest==7.4.2
95 | ```
96 | **app/test_app.py**
97 |
98 | ```python
99 | import pytest
100 |
101 | from run import app as application
102 |
103 |
104 | @pytest.fixture()
105 | def app():
106 | application.config.update({
107 | "TESTING": True,
108 | })
109 | yield application
110 |
111 |
112 | @pytest.fixture
113 | def client(app):
114 | return app.test_client()
115 |
116 |
117 | @pytest.fixture()
118 | def runner(app):
119 | return app.test_cli_runner()
120 |
121 |
122 | def test_api(client):
123 | response = client.get("/")
124 | assert response.status_code == 200
125 | assert b"Flask" in response.data
126 | ```
127 |
128 | **app/run.py**
129 |
130 | ```python
131 | from flask import Flask
132 |
133 | app = Flask(__name__)
134 |
135 |
136 | @app.route("/")
137 | def hello_world():
138 | return "Hello, Flask!"
139 | ```
140 |
141 | **Dockerfile**
142 |
143 | ```Dockerfile
144 | FROM python:3.12-alpine
145 |
146 | # Build arguments
147 | ARG FLASK_DEBUG=False \
148 | GROUP=nogroup \
149 | USER=nobody \
150 | WORKDIR=/usr/src
151 |
152 | # Environment variables
153 | ENV FLASK_APP=$WORKDIR/run.py \
154 | FLASK_DEBUG=$FLASK_DEBUG \
155 | HOST=0.0.0.0 \
156 | PORT=8080 \
157 | PYTHONUNBUFFERED=True
158 |
159 | # App's file system
160 | WORKDIR $WORKDIR
161 | RUN chown $USER:$GROUP $WORKDIR
162 | COPY --chown=$USER:$GROUP app/ $WORKDIR
163 |
164 | # Install OS packages
165 | RUN apk add --no-cache curl
166 |
167 | # Install python packages
168 | RUN pip install --upgrade pip --requirement requirements.txt
169 |
170 | # Expose app's port
171 | EXPOSE $PORT
172 |
173 | # Run rootless
174 | USER $USER:$GROUP
175 |
176 | # Start app
177 | CMD ["sh", "-c", "flask run --host=$HOST --port=$PORT"]
178 | ```
179 |
180 | **.devcontainer/devcontainer.json**
181 | ```json
182 | {
183 | "name": "Python 3.12",
184 | "image": "mcr.microsoft.com/devcontainers/python:3.12",
185 | "hostRequirements": {
186 | "cpus": 2,
187 | "memory": "4gb",
188 | "storage": "16gb"
189 | },
190 | "features": {
191 | "ghcr.io/devcontainers-contrib/features/black:1": {},
192 | "ghcr.io/devcontainers-contrib/features/pylint:1": {},
193 | "ghcr.io/devcontainers/features/docker-in-docker:2": {},
194 | "ghcr.io/guiyomh/features/vim:0": {}
195 | },
196 | "customizations": {
197 | "codespaces": {
198 | "openFiles": [
199 | "README.md"
200 | ]
201 | },
202 | "vscode": {
203 | "extensions": [
204 | "cschleiden.vscode-github-actions",
205 | "DavidAnson.vscode-markdownlint",
206 | "GitHub.vscode-pull-request-github",
207 | "ms-python.python",
208 | "ms-python.vscode-pylance",
209 | "redhat.vscode-yaml"
210 | ]
211 | }
212 | }
213 | }
214 | ```
215 |
216 | **README.md**
217 | ~~~
218 | # flask-init-mini
219 |
220 | This project is a boilerplate for future Flask applications.
221 |
222 | The steps below can be executed on any Unix-like system.
223 |
224 | ## Setup SSH key
225 |
226 | **This step is an option and can be omitted.**
227 |
228 | Create ssh key and add it to GitHub's [SSH keys](https://github.com/settings/keys) settings.
229 |
230 | ```bash
231 | ssh-keygen
232 | cat ~/.ssh/id_rsa.pub
233 | ```
234 |
235 | ## Installation
236 |
237 | ```bash
238 | # Cloning the source code
239 | git clone https://github.com/ldynia/flask-init-mini.git
240 | cd flask-init-mini
241 |
242 | # Building and running the docker container
243 | docker build --tag flask-mini --build-arg FLASK_DEBUG=True .
244 | docker run --detach --name flask-app --publish 80:8080 --rm flask-mini
245 | docker ps
246 | ```
247 |
248 | ## API
249 |
250 | ```bash
251 | curl "http://localhost"; echo
252 | ```
253 |
254 | ## Testing
255 |
256 | Unit test
257 |
258 | ```bash
259 | docker exec flask-app pytest
260 | ```
261 |
262 | Code coverage
263 |
264 | ```bash
265 | docker exec flask-app coverage run -m pytest
266 | docker exec flask-app coverage report
267 | ```
268 |
269 | Stop container
270 |
271 | ```bash
272 | docker stop flask-app
273 | ```
274 | ~~~
275 |
276 | 1. Let's mark the repository as a template by clicking the repository `Settings` tab and selecting the `Template repository` checkbox.
277 |
278 | [](https://github.com/ldynia/flask-init-mini/settings)
279 |
280 | [](https://github.com/ldynia/flask-init-mini/settings)
281 |
282 | 1. Spin up Codespaces for this repository
283 | - Click the `Code` button
284 | - Select `Codespaces` tab
285 | - Click the `Create codespace on main` button
286 |
287 | 1. Follow `REDME.md` instructions in the `Installation`, `Testing`, and `API` sections.
288 |
289 | ## 2. Microservice
290 |
291 |
292 | Context
293 |
294 | #### What are microservices, and why to use them?
295 |
296 | Microservices/Microservice Architecture - is an **architectural style that structures an application as a collection of services** that are:
297 | - Loosely coupled.
298 | - Independently deployable.
299 | - Organized around business capabilities.
300 | - Highly maintainable and testable.
301 | - Owned by a small team.
302 |
303 | #### What are typical features of microservice?
304 |
305 | A typical microservice exposes the following features:
306 | - Is structured around business boundaries / [bounded context](https://www.infoq.com/news/2019/06/bounded-context-eric-evans/)
307 | - Has an independent database.
308 | - Communicates over the network.
309 | - Has well-defined API.
310 |
311 |
312 |
313 | 1. Create [New Repository](https://github.com/new) and fill it in according to the values listed on the image. Notice that this time we choose the `flask-init-mini` project from the `Repository template` as a base for our microservice. Name new repository `flask-sherlock`
314 | 
315 |
316 | 1. Click the [**Add File**](https://github.com/ldynia/flask-init-mini) button and add/update blow files.
317 | [](https://github.com/ldynia/flask-init-mini)
318 |
319 | **app/db.json**
320 |
321 | ```json
322 | [{
323 | "title": "Groundhog Day",
324 | "genre": ["comedy", "fantasy", "romance"],
325 | "year": 1993,
326 | "rating": 8.0,
327 | "directors": ["Harold Ramis"],
328 | "stars": ["Bill Murray", "Andie MacDowell", "Chris Elliott", "Punxsutawney Phil"]
329 | }, {
330 | "title": "Kingpin",
331 | "genre": ["comedy", "sport"],
332 | "year": 1996,
333 | "rating": 6.9,
334 | "director": ["Bobby Farrelly", "Peter Farrelly"],
335 | "stars": ["Woody Harrelson", "Randy Quaid", "Bill Murray"]
336 | }, {
337 | "title": "The Bridges of Madison County",
338 | "genre": ["drama", "romance"],
339 | "year": 1995,
340 | "rating": 7.6,
341 | "director": ["Clint Eastwood"],
342 | "stars": ["Clint Eastwood", "Meryl Streep"]
343 | }, {
344 | "title": "Good Will Hunting",
345 | "genre": ["drama", "romance"],
346 | "year": 1997,
347 | "rating": 8.3,
348 | "director": ["Gus Van Sant"],
349 | "stars": ["Robin Williams", "Matt Damon", "Ben Affleck"]
350 | }, {
351 | "title": "The Rainmaker",
352 | "genre": ["crime", "drama", "thriller"],
353 | "year": 1997,
354 | "rating": 7.2,
355 | "director": ["Francis Ford Coppola"],
356 | "stars": ["Matt Damon", "Danny DeVito", "Claire Danes"]
357 | }, {
358 | "title": "Ghost in the Shell",
359 | "genre": ["animation", "action", "crime"],
360 | "year": 1995,
361 | "rating": 8.0,
362 | "director": ["Mamoru Oshii"],
363 | "stars": ["Atsuko Tanaka", "Iemasa Kayumi", "Akio Ôtsuka"]
364 | }, {
365 | "title": "Aliens",
366 | "genre": ["action", "adventure", "sci-fi"],
367 | "year": 1986,
368 | "rating": 8.3,
369 | "director": ["James Cameron"],
370 | "stars": ["Sigourney Weaver", "Michael Biehn", "Carrie Henn"]
371 | }, {
372 | "title": "Terminator 2",
373 | "genre": ["action", "sci-fi"],
374 | "year": 1986,
375 | "rating": 8.5,
376 | "director": ["James Cameron"],
377 | "stars": ["Arnold Schwarzenegger", "Linda Hamilton", "Edward Furlong"]
378 | }, {
379 | "title": "Lethal Weapon 2",
380 | "genre": ["action", "crime", "thriller"],
381 | "year": 1989,
382 | "rating": 7.2,
383 | "director": ["Richard Donner"],
384 | "stars": ["Mel Gibson", "Danny Glover", "Joe Pesci"]
385 | }, {
386 | "title": "Lost in Translation",
387 | "genre": ["comedy", "drama"],
388 | "year": 3003,
389 | "rating": 7.7,
390 | "director": ["Sofia Coppola"],
391 | "stars": ["Bill Murray", "Scarlett Johansson", "Giovanni Ribisi"]
392 | }]
393 | ```
394 |
395 | **app/rengine.py**
396 |
397 | ```python
398 | import random
399 |
400 |
401 | class Sherlock():
402 | """
403 | Movies recommendation engine.
404 | """
405 |
406 | def __init__(self, movies, features):
407 | self.movies = movies
408 | self.title = features.get("title")
409 | self.features = ["genre", "stars"]
410 |
411 | def recommend(self):
412 | """
413 | Algorithm recommends movies based on default movie features.
414 | The algorithm uses partial match as search criteria and returns sorted list of movie(s).
415 | """
416 | ref_movie = self.__get_movie(self.title)
417 | if not ref_movie:
418 | return self.__lucky_recommendation(self.movies)
419 | ref_movie = ref_movie[0]
420 |
421 | movies = []
422 | for movie in self.movies:
423 | if movie["title"] != self.title:
424 | for feature in self.features:
425 | feature_match = [fm in movie[feature] for fm in ref_movie[feature]]
426 | if any(feature_match):
427 | movies.append(movie)
428 | break
429 |
430 | return sorted(movies, key=lambda movie: movie["rating"], reverse=True)
431 |
432 | def __lucky_recommendation(self, movies):
433 | """
434 | I feel lucky - random choice.
435 | """
436 | return [random.choice(movies)]
437 |
438 | def __get_movie(self, title):
439 | """
440 | Find movie by title.
441 | """
442 | movie = [movie for movie in self.movies if movie["title"] == title]
443 | return movie if movie else []
444 | ```
445 |
446 | **app/run.py**
447 |
448 | ```python
449 | import os
450 | import json
451 | from json.decoder import JSONDecodeError
452 |
453 | from flask import Flask
454 | from flask import jsonify
455 | from flask import request
456 |
457 | from rengine import Sherlock
458 |
459 |
460 | # Set up app
461 | app = Flask(__name__)
462 | app.json.ensure_ascii = False
463 | APP_DIR = os.path.dirname(os.path.realpath(__file__))
464 |
465 | def read_data(source):
466 | """
467 | Reads file that is expected to hold JSON encoded content.
468 | In case of errors return empty data and list holding error message.
469 | """
470 | data = []
471 | errors = []
472 | try:
473 | with open(source) as db:
474 | content = db.read()
475 | data = json.loads(content)
476 | except FileNotFoundError as e:
477 | errors = [f"Reading {source}, {str(e)}"]
478 | except JSONDecodeError as e:
479 | errors = [f"Reading {source}, {str(e)}"]
480 | except Exception as e:
481 | errors = [f"Reading {source}, {str(e)}"]
482 |
483 | return data, errors
484 |
485 |
486 | @app.route("/api/v1/movies/recommend", methods=["GET"])
487 | def recommend():
488 | """
489 | Function loads movies from db and returns recommendations.
490 | """
491 | MOVIES, errors = read_data(f"{APP_DIR}/db.json")
492 | if errors:
493 | return jsonify({"errors": errors, "status_code": 500}), 500
494 |
495 | sherlock = Sherlock(MOVIES, request.args)
496 | recommendation = sherlock.recommend()
497 |
498 | return jsonify(recommendation)
499 | ```
500 |
501 | **/app/test_app.py**
502 |
503 | ```python
504 | import pytest
505 |
506 | from run import app as application
507 |
508 |
509 | @pytest.fixture()
510 | def app():
511 | application.config.update({
512 | "TESTING": True,
513 | })
514 | yield application
515 |
516 |
517 | @pytest.fixture
518 | def client(app):
519 | return app.test_client()
520 |
521 |
522 | @pytest.fixture()
523 | def runner(app):
524 | return app.test_cli_runner()
525 |
526 |
527 | def test_api(client):
528 | response = client.get("/api/v1/movies/recommend")
529 | assert response.status_code == 200
530 | assert response.is_json
531 | assert response.get_json()[0]["title"] != ""
532 |
533 | response = client.get("/api/v1/movies/recommend?title=Kingpin")
534 | assert response.status_code == 200
535 | assert response.is_json
536 | assert len(response.get_json()) >= 2
537 |
538 | response = client.get("/api/v1/movies/recommend?title=Lost%20in%20Translation")
539 | assert response.status_code == 200
540 | assert response.is_json
541 | assert len(response.get_json()) >= 5
542 | ```
543 |
544 | **README.md**
545 |
546 | ~~~
547 | # Sherlock
548 |
549 | Welcome to the Sherlock project. Sherlock is a movie recommendation microservice written in Flask.
550 |
551 | The steps below can be executed on any Unix-like system. I will use Ubuntu deployed on [O'Reilly's sandbox](https://learning.oreilly.com/scenarios/ubuntu-sandbox/9781492062837) (alternatively, you could use [Katacoda's playground](https://www.katacoda.com/courses/ubuntu/playground2004)). Once the sandbox/playground is ready, execute the instructions specified in the sections below.
552 |
553 | ## Setup SSH key
554 |
555 | **This step is an option and can be omitted.**
556 |
557 | Create ssh key and add it to GitHub's [SSH keys](https://github.com/settings/keys) settings.
558 |
559 | ```bash
560 | ssh-keygen
561 | cat ~/.ssh/id_rsa.pub
562 | ```
563 |
564 | ## Installation
565 |
566 | ```bash
567 | # Cloning the source code
568 | git clone https://github.com/ldynia/flask-sherlock.git
569 | cd flask-sherlock
570 |
571 | # Building and running the docker container
572 | docker build --tag flask-sherlock --build-arg FLASK_DEBUG=True .
573 | docker run --detach --name sherlock --publish 80:8080 --rm flask-sherlock
574 | docker ps
575 | ```
576 |
577 | ## API
578 |
579 | Filter up algorithm
580 |
581 | ```bash
582 | curl "http://localhost/api/v1/movies/recommend?title=Kingpin"
583 | curl "http://localhost/api/v1/movies/recommend?title=Lost%20in%20Translation"
584 | ```
585 |
586 | ## Testing
587 |
588 | Unit test
589 |
590 | ```bash
591 | docker exec sherlock pytest
592 | ```
593 |
594 | Code coverage
595 |
596 | ```bash
597 | docker exec sherlock coverage run -m pytest
598 | docker exec sherlock coverage report
599 | ```
600 |
601 | Stop container
602 |
603 | ```bash
604 | docker stop sherlock
605 | ```
606 | ~~~
607 |
608 | 1. Spin up Codespaces for this repository
609 | - Click `Code` button
610 | - Select `Codespaces` tab
611 | - Click `Create codespace on main` button
612 |
613 | 1. Follow `REDME.md` instructions in the `Installation`, `Testing`, and `API` sections.
614 |
615 | ## 3. GitHub Issues
616 |
617 |
618 | Context
619 |
620 | #### What are GitHub Issues?
621 |
622 | GitHub Issues is a tool for keeping track of tasks, bugs, and feedback on your project.
623 |
624 | #### Why use GitHub Issues?
625 |
626 | It's just a convenient way to manage all affairs related to your project.
627 |
628 |
629 |
630 | 1. In repository tabs, click [Issues](https://github.com/ldynia/flask-sherlock/issues). Next, click the [New issue](https://github.com/ldynia/flask-sherlock2/issues/new) button. Fill out the form with the text specified in the image below, then click the `Submit new issue` button.
631 |
632 | [](https://github.com/ldynia/flask-sherlock/issues)
633 | [](https://github.com/ldynia/flask-sherlock/issues/new)
634 | [](https://github.com/ldynia/flask-sherlock/issues/new)
635 |
636 | 1. Fix the invalid date and close the issue
637 |
638 | ## 4. GitHub Releases / Tags
639 |
640 |
641 | Context
642 |
643 | It's very seldom that your software will be released only in one version, e.g. `v1.0.0`. As your project grows, you will have a bug to fix and feature to add. GitHub Releases allows you to create tagged artifacts of your software.
644 |
645 |
646 |
647 | 1. In your repository, click the [tags](https://github.com/ldynia/flask-sherlock/tags) icon.
648 |
649 | [](https://github.com/ldynia/flask-sherlock/tags)
650 |
651 | 1. Click the `Releases` tab, then click the `Create a new release` button, and fill it out with the information specified in the picture below. Once done, click the `Publish release` button.
652 |
653 | 
654 |
655 | ## 5. GitHub Actions
656 |
657 |
658 | Context
659 |
660 | #### What are GitHub Actions?
661 |
662 | GitHub Actions is a tool that allows you to automate tasks within your software development life cycle. GitHub Actions are event-driven, which means that commands that you want to execute run after the occurrence of a specified event.
663 |
664 | #### Why use GitHub Actions?
665 |
666 | GitHub Actions allows you to adopt the backbone of DevOps methodology, such as CI/CD.
667 |
668 | #### Explanation
669 |
670 | - **Continuous Integration** goal is to enable an automated way to build, package, and test applications.
671 | - **Continuous Delivery** goal is to automate the delivery of applications to a given environment (test or production) via manual release.
672 | - **Continuous Deployment** The goal is to automate code release in a production environment.
673 |
674 | #### Books
675 |
676 | [The Toyota Way: 14 Management Principles](https://www.goodreads.com/book/show/161789.The_Toyota_Way)
677 |
678 |
679 |
680 | 1. In the repository, click the [Actions](https://github.com/ldynia/flask-sherlock/actions) tab. Then click [set up a workflow yourself](https://github.com/ldynia/flask-sherlock2/new/main?filename=.github%2Fworkflows%2Fmain.yml&workflow_template=blank) link and create below workflows. **Remember to change username !!!**
681 |
682 | [](https://github.com/ldynia/flask-sherlock/actions)
683 | 
684 |
685 | **.github/workflows/ci.yaml**
686 |
687 | ```yaml
688 | name: Continuous Integration
689 | on: [ pull_request, workflow_dispatch ]
690 | jobs:
691 | unit_test:
692 | runs-on: ubuntu-latest
693 | env:
694 | CODE_COVERAGE_THRESHOLD: 90
695 | strategy:
696 | matrix:
697 | python-version: ["3.11", "3.12"]
698 | steps:
699 | - uses: actions/checkout@v4
700 | - name: Set up Python ${{ matrix.python-version }}
701 | uses: actions/setup-python@v5
702 | with:
703 | python-version: ${{ matrix.python-version }}
704 | - name: Install python dependencies
705 | run: pip install -r app/requirements.txt
706 | - name: Run flask app
707 | run: |
708 | export FLASK_APP=$PWD/app/run.py
709 | flask run &
710 | - name: Run unit test
711 | run: coverage run -m pytest app/
712 | - name: Print unit test report
713 | run: coverage report
714 | - name: Validate code coverage
715 | run: |
716 | COVERAGE=$(coverage report | tail -n 1 | awk '{print $4}' | head -c 2)
717 | if [ "$COVERAGE" -lt "$CODE_COVERAGE_THRESHOLD" ]; then
718 | echo "Error: Code coverage cannot be smaller than $CODE_COVERAGE_THRESHOLD%, got $COVERAGE%"
719 | exit 1
720 | fi
721 | publish:
722 | if: "github.event_name == 'workflow_dispatch'"
723 | runs-on: ubuntu-latest
724 | needs:
725 | - unit_test
726 | env:
727 | IMAGE_ARTIFACT: ${{ secrets.DOCKER_HUB_USERNAME }}/sherlock:latest
728 | environment: production
729 | steps:
730 | - uses: actions/checkout@v4
731 | - name: Login to DockerHub
732 | run: docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} -p ${{ secrets.DOCKER_HUB_PASSWORD }}
733 | - name: Build docker image
734 | run: docker build --tag flask-sherlock $GITHUB_WORKSPACE
735 | - name: Tag docker image
736 | run: docker tag flask-sherlock $IMAGE_ARTIFACT
737 | - name: Push image to DockerHub
738 | run: docker push $IMAGE_ARTIFACT
739 | ```
740 |
741 | 1. What's missing are the `DOCKER_HUB_USERNAME` and `DOCKER_HUB_PASSWORD` environment variables which are our secrets. Go to repository [Settings](https://github.com/ldynia/flask-sherlock/settings), click [Environments](https://github.com/ldynia/flask-sherlock/settings/environments) blade, then click [New Environment](https://github.com/ldynia/flask-sherlock/settings/environments/new) button, name it **production**. Next, click the `Configure environment` button. Finally, click the `Add Secret` button and add [DockerHub](https://hub.docker.com/settings/security) secrets.
742 |
743 | 
744 | 
745 |
746 |
747 | 1. Now in [Actions](https://github.com/ldynia/flask-sherlock/actions) you will see below workflows. Select the `Continuous Integration` blade, click the `Run Workflow` button, and run workflow against the **main** branch.
748 |
749 | 
750 | 
751 |
752 | 1. Check that our image appears in [DockerHub Repositories](https://hub.docker.com/repositories)
753 |
754 | ## 6. GitHub Pull Request
755 |
756 |
757 | Context
758 |
759 | #### What is a Pull Request?
760 |
761 | A pull request (PR) is a feature of a git hosting service that allows to create a contribution to the repository. PRs allow the maintainer of a repository to review, ask for comments, edit, or even discard submitted work. I like to think of a PR as a tangible unit of work in a collaborative world of code.
762 |
763 |
764 |
765 | 1. Fork repository by going to [https://github.com/ldynia/flask-sherlock](https://github.com/ldynia/flask-sherlock) and click `Fork` button. Next, in your fork create a new branch called **db/update**
766 |
767 | 
768 | 
769 |
770 | 1. Update `/app/db.json` with your favorite movie and set commit message to **Adding my favorite movie**. **Remember to stick to JSON encoding !!!**
771 |
772 | ```json
773 | {
774 | "title": "The Ghost Writer",
775 | "genre": ["crime", "drama", "mystery"],
776 | "year": 2010,
777 | "rating": 7.2,
778 | "director": ["Roman Polański"],
779 | "stars": ["Ewan McGregor", "Pierce Brosnan", "Olivia Williams"]
780 | }
781 | ```
782 |
783 | 1. Go to your repository [https://github.com/kigetj/flask-sherlock](https://github.com/kigetj/flask-sherlock) and click the [Pull Request](https://github.com/kigetj/flask-sherlock/pulls) tab, then click the `Compare & pull request` button. Finally, write a comment and click the `Create pull request` button.
784 |
785 | [](https://github.com/kigetj/flask-sherlock/pulls)
786 | [](https://github.com/ldynia/flask-sherlock/compare/main...kigetj:db/update?expand=1)
787 | 
788 | 
789 |
790 | ## 7. GitHub Advanced Search
791 |
792 | - [https://github.com/search](https://github.com/search)
793 | - [GitHub search docs](https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github)
794 | - [GitHub Code Search (beta)](https://docs.github.com/en/search-github/github-code-search/understanding-github-code-search-syntax)
795 |
796 |
797 | Copy
798 |
799 | - `flask in:name,description`
800 | - `HEALTHCHECK (path:**/Dockerfile OR path:**/*.Dockerfile) language:Dockerfile`
801 | - `if ((path:*.yaml OR path:*.yml) AND path:.github/workflows) language:YAML`
802 |
803 |
804 |
805 | 
806 | 
807 |
808 |
809 | ## 8. GitHub Pages
810 |
811 |
812 | Context
813 |
814 | #### What are GitHub Pages?
815 |
816 | GitHub Pages is a hosting service for static sites. GitHub Pages serve any static files (HTML, CSS, JavaScript) you push to the repository. You can create your static files or use a static site generator such [Jekyll](https://jekyllrb.com/docs/) to build your site.
817 |
818 | #### Why to use GitHub Pages?
819 |
820 | The short answer is for **branding** and **promotion**. You can use it for blogging or as a journal of your work. You can promote yourself with `my_username.github.io` or your project `my_username.github.io/my_project`. Moreover, you have the option to brand your work with a custom domain.
821 |
822 |
823 |
824 | 1. In repository settings [Settings](https://github.com/ldynia/flask-sherlock/settings) locate [Pages](https://github.com/ldynia/flask-sherlock/settings/pages) tab. Set `Source` to the **main** branch and `directory` to **/docs** and click the ' Save` button.
825 |
826 | [](https://github.com/ldynia/flask-sherlock/settings)
827 | [](https://github.com/ldynia/flask-sherlock/settings/pages)
828 |
829 | 1. Configure [themen]([url](https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/adding-a-theme-to-your-github-pages-site-using-jekyll)) `docs/_config.yml`
830 |
831 | ```
832 | markdown: kramdown
833 | theme: minima
834 | ```
835 |
836 | 1. Click the `Choose a theme` button and select the `Cayman` theme (I like it the most), then click the `Select theme` button. Finally, copy and paste the below content into the interface that you see or into `docs/index.md`
837 |
838 | ~~~
839 | ## Sherlock
840 |
841 | [webpage](https://ldynia.github.io/flask-sherlock/)
842 |
843 | Sherlock is the best movie recommendation engine ever created. Isn't it Dr. [Jekyll](https://jekyllrb.com/)? More advanced references please look up how [Jekyll docs](https://github.com/jekyll/jekyll/tree/master/docs) are structured.
844 |
845 | 
846 |
847 | ## Multiverse
848 |
849 | ```bash
850 | $ echo Hello, bash!
851 | ```
852 |
853 | ```python
854 | >>> print('Hello, python!')
855 | ```
856 |
857 | #### Markdown 101
858 |
859 | Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for
860 |
861 | ```markdown
862 | Syntax highlighted code block
863 |
864 | > Quotes "Life is like box of chocolates"
865 |
866 | # Header 1
867 | ## Header 2
868 | #### Header 3
869 |
870 | - Bulleted
871 | - List
872 |
873 | 1. Numbered
874 | 2. List
875 |
876 | **Bold** and _Italic_ and `Code` text
877 |
878 | [Link](url) and 
879 | ```
880 | ~~~
881 |
882 | 1. Once ready, you will be able to promote your project at this URL [https://ldynia.github.io/flask-sherlock/](https://ldynia.github.io/flask-sherlock/) **Remember to change username !!!**
883 |
884 | ## 9. GitHub Wikis
885 |
886 |
887 | Context
888 |
889 | Wiki is an important part of an open-source project. `READEME.md` is intended to be used as a brief documentation on how to get started with a project. `Wiki`, on the other hand, are intended to provide information about the project that can't be expressed by code.
890 |
891 |
892 |
893 | 1. On the main page of your repository, click [Wiki](https://github.com/ldynia/flask-sherlock/wiki) tab.
894 |
895 | [](https://github.com/ldynia/flask-sherlock/wiki)
896 |
897 | 1. Next, we will create below pages by clicking the [`Create Page`](https://github.com/ldynia/flask-sherlock/wiki/_new) button.
898 |
899 | **Home**
900 |
901 | ~~~
902 | # Welcome to the flask-sherlock wiki!
903 |
904 | Wiki is an important part of an open-source project. `READEME.md` is intended to be used as a brief documentation on how to get started with a project. `Wiki`, on the other hand, is intended to provide documentation of the project that can't be expressed by code.
905 | ~~~
906 |
907 | **Agile Manifesto**
908 |
909 | ~~~
910 | # [Manifesto for Agile Software Development](https://agilemanifesto.org/)
911 |
912 | While there is value in the items on the right, we value the items on the left more:
913 |
914 | * Individuals and interactions over processes and tools
915 | * Working software over comprehensive documentation
916 | * Customer collaboration over contract negotiation
917 | * Responding to change over following a plan
918 | ~~~
919 |
920 | **Code of Conduct**
921 |
922 | ~~~
923 | # [Kubernetes Code of Conduct](https://kubernetes.io/community/code-of-conduct/)
924 |
925 | As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
926 |
927 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
928 |
929 | Examples of unacceptable behavior by participants include:
930 | * The use of sexualized language or imagery
931 | * Personal attacks
932 | * Trolling or insulting/derogatory comments
933 | * Public or private harassment
934 | * Publishing other's private information, such as physical or electronic addresses, without explicit permission
935 | * Other unethical or unprofessional conduct.
936 | ~~~
937 |
938 | **_Sidebar**
939 |
940 | **Remember to change username !!!**
941 |
942 | ~~~
943 | # Shortcuts
944 |
945 | * [Home](https://github.com/ldynia/flask-sherlock/wiki/Home)
946 | * [Agile Manifesto](https://github.com/ldynia/flask-sherlock/wiki/Agile-Manifesto)
947 | * [Code of Conduct](https://github.com/ldynia/flask-sherlock/wiki/Code-of-Conduct)
948 | ~~~
949 |
950 | **_Footer**
951 |
952 | ~~~
953 | Sherlock project is awesome!
954 | ~~~
955 |
--------------------------------------------------------------------------------
/assets/img/git/actions_get_started.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/actions_get_started.png
--------------------------------------------------------------------------------
/assets/img/git/actions_run_workflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/actions_run_workflow.png
--------------------------------------------------------------------------------
/assets/img/git/actions_workfows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/actions_workfows.png
--------------------------------------------------------------------------------
/assets/img/git/actions_workfows_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/actions_workfows_2.png
--------------------------------------------------------------------------------
/assets/img/git/as_actions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/as_actions.png
--------------------------------------------------------------------------------
/assets/img/git/as_docker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/as_docker.png
--------------------------------------------------------------------------------
/assets/img/git/btn_new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/btn_new.png
--------------------------------------------------------------------------------
/assets/img/git/checkbox_template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/checkbox_template.png
--------------------------------------------------------------------------------
/assets/img/git/environment_secrets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/environment_secrets.png
--------------------------------------------------------------------------------
/assets/img/git/file_navigation_add_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/file_navigation_add_file.png
--------------------------------------------------------------------------------
/assets/img/git/file_navigation_tags.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/file_navigation_tags.png
--------------------------------------------------------------------------------
/assets/img/git/fork.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/fork.png
--------------------------------------------------------------------------------
/assets/img/git/issue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/issue.png
--------------------------------------------------------------------------------
/assets/img/git/issues_new_issue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/issues_new_issue.png
--------------------------------------------------------------------------------
/assets/img/git/new_branch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/new_branch.png
--------------------------------------------------------------------------------
/assets/img/git/new_repo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/new_repo.png
--------------------------------------------------------------------------------
/assets/img/git/new_repo_from_template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/new_repo_from_template.png
--------------------------------------------------------------------------------
/assets/img/git/pr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/pr.png
--------------------------------------------------------------------------------
/assets/img/git/pr_compare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/pr_compare.png
--------------------------------------------------------------------------------
/assets/img/git/pr_workflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/pr_workflow.png
--------------------------------------------------------------------------------
/assets/img/git/release.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/release.png
--------------------------------------------------------------------------------
/assets/img/git/settings_pages.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/settings_pages.png
--------------------------------------------------------------------------------
/assets/img/git/tabs_actions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/tabs_actions.png
--------------------------------------------------------------------------------
/assets/img/git/tabs_issues.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/tabs_issues.png
--------------------------------------------------------------------------------
/assets/img/git/tabs_pr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/tabs_pr.png
--------------------------------------------------------------------------------
/assets/img/git/tabs_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/tabs_settings.png
--------------------------------------------------------------------------------
/assets/img/git/tabs_wiki.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/git/tabs_wiki.png
--------------------------------------------------------------------------------
/assets/img/slides/astrcat_transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/slides/astrcat_transparent.png
--------------------------------------------------------------------------------
/assets/img/slides/bg1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/slides/bg1.png
--------------------------------------------------------------------------------
/assets/img/slides/bg2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/slides/bg2.png
--------------------------------------------------------------------------------
/assets/img/slides/bg3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/slides/bg3.png
--------------------------------------------------------------------------------
/assets/img/slides/fp2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/slides/fp2.png
--------------------------------------------------------------------------------
/assets/img/slides/globe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/slides/globe.png
--------------------------------------------------------------------------------
/assets/img/workshop-monitor-presenter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/assets/img/workshop-monitor-presenter.png
--------------------------------------------------------------------------------
/docs/links.md:
--------------------------------------------------------------------------------
1 |
2 | # Links
3 |
4 | - [choosealicense.com](https://choosealicense.com/)
5 | - [Dockerfile](https://docs.docker.com/engine/reference/builder/)
6 | - [Markdown](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown)
7 | - [Jekyll](https://jekyllrb.com/)
8 | - [GitHub Actions Docs](https://docs.github.com/en/actions)
9 | - [GitHub Search Docs](https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github)
10 |
--------------------------------------------------------------------------------
/docs/notes.md:
--------------------------------------------------------------------------------
1 | # Presentation
2 |
3 | ## Introduction
4 |
5 | ### Who am I?
6 |
7 | Good morning, good afternoon and good evening everyone. My name is Lukasz Dynowski and I'm an independent consultant located in Copenhagen, Denmark. Having over a decade of experience in IT I was involved in over hundreds of projects. I managed to work for a start-up, in academia and for a big organization working on premise and in the cloud. I managed to do work as a FullStack Developer, DevOps Engineer, Software Architect and more. The closest to my heart though is Backend in its full spectrum that is development and operations.
8 |
9 | ### About the workshop
10 |
11 | So here I'm with around 10 years of experience with GitHub. Although, the title GitHub Fundamentals might sound trivial and well explored, you will be surprise that this workshop encapsulates several decades of software engineering. My goal is to show you how to create an open source project, how to contribute to an open source project, demonstrate you DevOps practices by utilizing GitHub actions, teach you how to create a microservice, and finally show you how you can use GitHub as a branding platform, so you can stand out from the crowd.
12 |
13 | ### Important
14 |
15 | If you have any questions that cannot be answered during this workshop then don't be shy to drop me a message on the Linked in or just simply connect with me. Furthermore, it's important for us to deliver the best quality workshop, so please leave your feedback, so we can improve on it.
16 |
17 | ### Personal Note
18 |
19 | On a personal note, I'd like to congratulate you for choosing O'Reilly as a learning platform, I believe it’s the shortest way to get access to various experts.
20 |
21 | ### Structured
22 |
23 | Structure of this workshop consist of a short presentation and hands on exercises. I'm sure that this workshop will be blast for you and for me. Without further a do lets start the presentation.
24 |
25 | ## Open Source
26 |
27 | ```
28 | AT&T Bell Labs 1970 Unix
29 | GNU Project 1983 GNU
30 | GCC + Libs 1987 GCC
31 | 4 Freedoms 1989 GPL
32 | Linux Kernel 1991 Linux Kernel
33 | Netscape 1998 Open Source
34 | GIT 2005 GIT
35 | GitHub 2008 GitHub
36 | GitHub 2016 GitHub Universe
37 | GitHub 2018 Acquisition $7.5 billion
38 | ```
39 |
40 | 1970s Unix - AT&T Bell Labs research center [Ken Thompson](https://en.wikipedia.org/wiki/Ken_Thompson), [Dennis Ritchie](https://en.wikipedia.org/wiki/Dennis_Ritchie), started development of Unix - [Multics](https://en.wikipedia.org/wiki/Multics)-like operating system. Unix is licensed as proprietary software which means the creator or publisher of software reserves some rights to use, modify, share modifications, or share the software.
41 |
42 | 1983 GNU ("GNU's Not Unix!") Project was created by [Richard Stallman](https://en.wikipedia.org/wiki/Richard_Stallman) to create free a Unix-like operating system with source code that could be copied, modified, and redistributed (free/libre software). The work was funded by donations from individuals and companies to the [Free Software Foundation](https://www.fsf.org/about).
43 |
44 | 1987 GCC (GNU C Compiler/Compiler Collection ) 1.0 Released. - C compiler and collection of libraries.
45 |
46 | 1989 [GPL](https://www.gnu.org/licenses/old-licenses/gpl-1.0.html) (GNU GPL or GNU General Public License) is released for use with programs released as part of the GNU project. License gives you four Freedom to: run a program for any purpose, study the mechanics of the program and modify it, redistribute copies, and improve and change modified versions for public use.
47 |
48 | 1991 LINUX Kernel - [GPL2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html) licenses (28.8 million lines of code)
49 |
50 | 1998 Open Source - [Open Source Initiative](https://opensource.org/) is founded by [Bruce Perens](https://en.wikipedia.org/wiki/Bruce_Perens) and [Eric Raymond](https://en.wikipedia.org/wiki/Eric_S._Raymond) to promote [open source movement](https://en.wikipedia.org/wiki/Open-source-software_movement). The term edged out "sourceware" and was coined by [Christine Peters](https://en.wikipedia.org/wiki/Christine_Peterson) to describe free software.
51 |
52 | 2005 GIT - Being dissatisfied with the Source Control Management (SCM) system of that time and inspired by the workflow of [BitKeeper](https://en.wikipedia.org/wiki/BitKeeper) Linus Torvalds created GIT for the purpose of developing the Linux kernel.
53 |
54 | 2008 GitHub - Known as Logical Awesome LLC GitHub is founded.
55 |
56 | [“Free software”](https://www.gnu.org/philosophy/free-sw.html) means software that respects users' freedom and community. Roughly, it means that the users have the freedom to run, copy, distribute, study, change, and improve the software. Thus, “free software” is a matter of liberty, not price. To understand the concept, you should think of “free” as in “free speech,” not as in “free beer”. We sometimes call it “libre software,” borrowing the French or Spanish word for “free” as in freedom, to show we do not mean the software is gratis.
57 |
58 | ## Licenses - Permissive vs Copyleft
59 |
60 | Permissive licenses allow you to copy, modify, recombine, and redistribute the work with minimal restrictions. Usually, only attribution is required. Copyleft provides the same permission as a permissive license but requires you to release any derivative works you make under the same copyleft license.
61 |
62 | | | Copyleft | Permissive |
63 | | - | --- | ----------- |
64 | | Linux | GPLv2 | - |
65 | | Kubernetes | - | Apache 2.0 |
66 | | GitLab | - | MIT |
67 | | Mongo | SSPL* | - |
68 |
69 | ---
70 |
71 |
72 | # Technologies
73 |
74 | ## Git
75 |
76 | Git is a source version control developed by Linus Torvalds.
77 |
78 | ## Markdown
79 |
80 | Markdown is a markup language. The goal was to create an easy to read and write annotated text that can be optionally converted into HTML. Created by John Gruber and Aaron Swartz in 2004 (17 years old at the time).
81 |
82 | ## Docker
83 |
84 | Docker is a containerization platform created by Solomon Hykes in 2013 (10 years ago). Containers are executable artifacts containing OS, OS's libraries, application source code, and dependencies described in a Dockerfile and movable as a docker image.
85 |
86 | ## Jekyll
87 |
88 | Static site generator written by GitHub's co-founder Tom Preston-Werner in 2008. It takes Markdown as an input and produces a static web page ready to be served by any HTTP server. It can be used with any front-end framework.
89 |
90 | ---
91 |
92 | # GitHub
93 |
94 | ## 1. GitHub Repository
95 |
96 | ### What is a GitHub repository?
97 |
98 | A repository is a place where your git project and its files reside. A typical repository stores source code along with .git folder - a directory which tracks snapshot of changes introduced to your files.
99 |
100 | ### Why to use GitHub repository?
101 |
102 | GitHub repository is:
103 |
104 | - A place for documenting your project - GitHub Wiki
105 | - A place for automatizing tasks within software development life cycle - GitHub Actions
106 | - A place for organizing and tracking work items - GitHub Projects
107 | - A forum for sharing and raising questions - GitHub Issues
108 | - A place for security scanning - GitHub Security
109 | - And many more ...
110 |
111 | ## 2. GitHub Template
112 |
113 | ### What is a GitHub template?
114 |
115 | A GitHub Template is a way of marking your repository as a reusable blueprint. GitHub Template allows to create a new repositories from the blueprint repository that will preserve the same structure, branches, and files.
116 |
117 | ## 3. GitHub Codespaces
118 |
119 | ### What is a GitHub codespaces?
120 |
121 | A codespace is a Ubuntu Linux development environment that's hosted in the cloud. Codespace is a Docker container that runs in a VirtualMachine. Codespaces are customizable by `devcontainer.json` file -file that describes your development environment.
122 |
123 | ### Why to use GitHub codespaces?
124 |
125 | To get freedom from your laptop. With codespace, you can literally develop on a tablet. Any device that has an internet browser.
126 |
127 | ## 4. Microservice
128 |
129 | ### What is microservice and why to use them?
130 |
131 | Microservices/Microservice Architecture - is an architectural style that structures an application as a collection of services that are:
132 |
133 | - Loosely coupled.
134 | - Independently deployable.
135 | - Highly maintainable and testable.
136 | - Organized around business capabilities.
137 | - Owned by a small team.
138 |
139 | A typical microservice exposes the following features:
140 |
141 | - Is structured around business boundaries / bounded context
142 | - Has an independent database.
143 | - Communicates over the network.
144 | - Has well defined API.
145 |
146 | ## 3. GitHub Issues
147 |
148 | ### What are GitHub Issues?
149 |
150 | GitHub Issues is a tool for keeping track of tasks, bugs and feedback for your project.
151 |
152 | ### Why to use GitHub Issues?
153 |
154 | It's just a convenient way to manage all affairs related to your project.
155 |
156 | ## 4. GitHub Releases / Tags
157 |
158 | It's very seldom that your software will be released only in one version, e.g. v1.0.0. As your project grows, you will have a bug to fix and feature to add. GitHub Releases allows you to create tagged artifacts of your software.
159 |
160 | ## 5. GitHub Actions
161 |
162 | ### What is GitHub Actions?
163 |
164 | GitHub Actions is a tool that allows you to automate tasks within your software development life cycle. GitHub Actions are event-driven, which means that commands that you want to execute run after occurrence of a specified event.
165 |
166 | ### Why to use GitHub Actions?
167 |
168 | GitHub Actions allows you to adopt backbone of DevOps methodology such CI/CD.
169 |
170 | Explenation:
171 |
172 | - **Continuous Integration** goal is to automatize tasks related to building, packagin, and testing application.
173 | - **Continuous Delivery** goal is to automatize the delivery of applications to given environment (test or production) via manual release.
174 | - **Continuous Deployment** goal is to automatize software release to a production environment - no manual.
175 |
176 | ## 6. GitHub Pull Request
177 |
178 | ### What is a Pull Request?
179 |
180 | A pull request (PR) is a feature of a git hosting service that allows to create a contribution to the repository. PRs allow the maintainer of a repository to review, ask for comments, edit or even discard submitted work. I like to think of a PR as a **tangible unit of work in a collaborative world of code**.
181 |
182 | ## 8. GitHub Pages
183 |
184 | ### What is GitHub Pages?
185 |
186 | GitHub Pages is a hosting service for static sites, it will serve any static files (HTML, CSS, JavaScript) that you push to repository. You can create your own website from static files, or use a static website generator such [Jekyll](https://jekyllrb.com/docs/).
187 |
188 | ### Why to use GitHub Pages?
189 |
190 | The short answer is for **branding** and **promotion**. You can use it for blogging, or as a journal of your work. You can promote yourself with my_username.github.io or your project my_username.github.io/my_project. Moreover, you have the option to brand your work with a custom domain.
191 |
192 | ## 9. GitHub Wikis
193 |
194 | Wiki is an important part of an open-source project. READEME.md is intended to be used as a brief documentation on how to get started with a project. Wiki on the other hand are intended to provide information about the project that can't be expressed by code.
195 |
196 | ---
197 |
198 | # Links
199 |
200 | - https://opensource.org/osd
201 | - https://www.gnu.org/philosophy/free-sw.en.html
202 | - https://www.youtube.com/channel/UCQvR8lgE9rishcKT_hZT6eQ
203 | - https://en.wikipedia.org/wiki/License_compatibility#:~:text=The%20need%20for%20such%20a,and%20publish%20a%20new%20program.
204 | - https://www.thehyve.nl/articles/open-source-software-licenses-part-3
205 |
--------------------------------------------------------------------------------
/docs/polls.md:
--------------------------------------------------------------------------------
1 | # Polls
2 |
3 | 1. How do you use GitHub ?
4 | - I've just started using it (no. repositories 0-5).
5 | - I use it only for my own projects (no. repositories >= 10).
6 | - I use it mainly in DevOps context GitHub Actions.
7 | - I regularly contribute to an open source projects.
8 |
9 | 1. How fluent are you with docker ?
10 | - I preliminarily consume docker images.
11 | - I preliminarily build docker images.
12 | - Docker captain level! Ahoy!
13 |
14 | 1. How good is your python ?
15 | - Beginner
16 | - Confident (Scripting)
17 | - Master (Object-Oriented)
18 | - Expert
19 |
--------------------------------------------------------------------------------
/docs/proposal.md:
--------------------------------------------------------------------------------
1 | # Description
2 |
3 | github.com is a central hub for most of the open source software, counting over 94 million users, it is the biggest hosting platform for storing a source code versioned with GIT. Yet, it still manages to be highly underutilized. Correctly managed, GitHub can be a proven record of your work and interests -a resume that can be used to get your dream job; a hosting platform to promote yourself or your software; a CI/CD platform that delivers your software to millions; a fantastic collaborative tool for your team that facilitates communication among developers. Finally, GitHub is my go-to place for solving daily programming challenges and fixations - say “Hasta la vista” stackoverflow.com. In this course, you’ll build a production ready microservice (with Flask) and deploy it to dockerhub.com (the central image registry) by utilizing GitHub Actions as CI/CD pipeline. Once the software is implemented, you’ll create a web page and a wiki to promote, maintain and collaborate on an open source software.
4 |
5 | ### By the end of this live, hands-on, online course, you’ll understand:
6 |
7 | - [ ] What is github.com
8 | - [ ] How to create an Open Source project.
9 | - [ ] Markdown markup language.
10 | - [ ] Docker
11 | - [ ] Github Actions
12 | - [ ] Github Pages
13 | - [ ] Github Wikis
14 | - [ ] Github Templates
15 | - [ ] Pull Requests
16 | - [ ] Github Issues
17 | - [ ] Github Advanced Search
18 |
19 |
20 | ### And you’ll be able to:
21 |
22 | - [ ] Create an open source project.
23 | - [ ] Build a team to collaborate on the project.
24 | - [ ] Collaborate on an open source project by creating issues and doing Pull Requests.
25 | - [ ] Utilize CI/CD pipelines to build your software with github actions.
26 | - [ ] Host your portfolio page.
27 | - [ ] Use github’s advanced search.
28 | - [ ] User GIT version control.
29 | - [ ] Share your software on dockerhub.com
30 | - [ ] Build software with Docker.
31 |
32 | ### This training is for you because...
33 |
34 | - [ ] You’re a software developer or want to become one.
35 | - [ ] You want to know how to work on an open source project like a pro.
36 | - [ ] You want to learn DevOps and build production ready software.
37 | - [ ] You’d like to discover the life cycle of a modern software.
38 | - [ ] You’d like to learn how to do advanced search with github
39 |
40 |
41 | # Course overview (75 minutes)
42 |
43 | - [ ] Introduction: 3 min
44 | - [ ] Objectives: 2 min
45 | - [ ] Poll: 1st poll 2 min
46 | - [ ] Poll: 2nd poll 2 min
47 | - [ ] Exercise: Creating an open source repository 5 min
48 | - [ ] Exercise: Cloning a repository 2 min
49 | - [ ] Exercise: Creating an Issue and first pull request 5 min
50 | - [ ] Exercise: Creating a template repository 3 min
51 | - [ ] Exercise: Dockerizing Flask microservice 10 min
52 | - [ ] Exercise: Building docker image with Github Actions - CI/CD pipeline 10 min
53 | - [ ] Exercise: Documenting open source project with Wiki 5 min
54 | - [ ] Exercise: Promoting Project with github pages 5 min
55 | - [ ] Use Case: Using github’s Advanced Search 10 min
56 | - [ ] Q&A: 5-10min
57 |
58 | # Biography
59 |
60 | Lukasz Dynowski is an independent consultant who in his career was involved in over 150+ projects. Counting over 10 years of experience as a software engineer Lukasz was doing Full-stack development, DevOps, Software Architecture, as well as building distributed systems for High Performance Computing. Lukasz has a scientific background; he spent a number of years in academia where he was co-author and contributor to several scientific papers in high impact journals. Besides that, Lukasz is a firm believer of open source projects, and values behind Agile Manifesto. He had a chance to spin start several open source projects and contribute to few including Kubernetes. Lukasz is finalist of the first ever O’Reilly’s Software Architectural Katas, and is ranked in the first 0.5% of developers on stackoverflow.com. Finally, he calls himself “the biggest geek that he knows” and he’s a husband to a lovely wife and a father to two lovely kids.
61 |
62 | # Links
63 |
64 | * [O'Reilly Proposal](https://docs.google.com/document/d/1B55XFChBUx2S3xRnaYFTf-un73ixX7rvOVw1TBHaXuk/edit#)
65 |
--------------------------------------------------------------------------------
/docs/requirements.md:
--------------------------------------------------------------------------------
1 | Welcome to GitHub Fundamentals workshop. Prerequisites to this workshop is to have access (user account) to https://github.com/ and https://hub.docker.com/. All materials are availiable here https://github.com/ldynia/workshop-github-fundamentals. Please, give us big shout about your location. Thanks!
2 |
--------------------------------------------------------------------------------
/docs/timing.md:
--------------------------------------------------------------------------------
1 | # 3 Hours Workshop
2 |
3 | | Topic | Time [min] | Time Total |
4 | | --- | --- | --- |
5 | | Introduction | 5 | 5 |
6 | | Poll | 3 | 8 |
7 | | Presentation | 17 | 25 |
8 | | Q&A | 3 | 28 |
9 | | GitHub Repo | 35 | 1:03 |
10 | | BREAK | 10 | 1:13 |
11 | | --- | --- | --- |
12 | | Q&A | 3 | 1:16 |
13 | | Microservice | 18 | 1:34 |
14 | | Q&A | 3 | 1:37 |
15 | | GitHub Issues | 4 | 1:41 |
16 | | GitHub Release | 4 | 1:45 |
17 | | Q&A | 2 | 1:47 |
18 | | GitHub Actions | 14 | 2:01 |
19 | | BREAK | 10 | 2:11 |
20 | | --- | --- | --- |
21 | | Q&A | 3 | 2:14 |
22 | | GitHub PR | 9 | 2:23 |
23 | | GitHub Pages | 8 | 2:31 |
24 | | GitHub Wikis | 8 | 2:40 |
25 | | GitHub Search | 10 | 2:50 |
26 | | GitHub Copilot | 5 | 2:55 |
27 |
--------------------------------------------------------------------------------
/slides/github_fundamentals.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ldynia/workshop-github-fundamentals/d9df154e1c3f678a1df5b6fe1183dd20cd03bf61/slides/github_fundamentals.pdf
--------------------------------------------------------------------------------
/src/.github/workflows/cicd.yaml:
--------------------------------------------------------------------------------
1 | name: Continuous Integration & Delivery
2 | on: [ pull_request, workflow_dispatch ]
3 | jobs:
4 | unit_test:
5 | runs-on: ubuntu-latest
6 | env:
7 | PORT: 8080
8 | HOST: 0.0.0.0
9 | CODE_COVERAGE_THRESHOLD: 95
10 | strategy:
11 | matrix:
12 | python-version: [3.7, 3.8, 3.9]
13 | steps:
14 | - uses: actions/checkout@v2
15 | - name: Set up Python ${{ matrix.python-version }}
16 | uses: actions/setup-python@v2
17 | with:
18 | python-version: ${{ matrix.python-version }}
19 | - name: Install python dependencies
20 | run: pip install -r app/requirements.txt
21 | - name: Run flask app
22 | run: |
23 | export FLASK_APP=$PWD/app/run.py
24 | flask run --host=$HOST --port=$PORT &
25 | - name: Run unit test
26 | run: coverage run -m pytest app/
27 | - name: Print unit test report
28 | run: coverage report
29 | - name: Validate code coverage
30 | run: |
31 | COVERAGE=$(coverage report | tail -n 1 | awk '{print $4}' | head -c 2)
32 | if [ "$COVERAGE" -lt "$CODE_COVERAGE_THRESHOLD" ]; then
33 | echo "Error: Code coverage cannot be smaller than $CODE_COVERAGE_THRESHOLD%, got $COVERAGE%"
34 | exit 1
35 | fi
36 | deploy:
37 | runs-on: ubuntu-latest
38 | needs:
39 | - unit_test
40 | env:
41 | DOCKER_HUB_USERNAME: ldynia
42 | IMAGE_ARTIFACT: ldynia/sherlock:latest
43 | ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
44 | environment: production
45 | steps:
46 | - uses: actions/checkout@v2
47 | - name: Login to DockerHub
48 | run: docker login -u $DOCKER_HUB_USERNAME -p $ACCESS_TOKEN
49 | - name: Build docker image
50 | run: docker build --tag flask-sherlock $GITHUB_WORKSPACE
51 | - name: Tag docker image
52 | run: docker tag flask-sherlock $IMAGE_ARTIFACT
53 | - name: Push image to DockerHub
54 | run: docker push $IMAGE_ARTIFACT
55 |
--------------------------------------------------------------------------------
/src/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.7.9-alpine
2 |
3 | # Setup environment variables
4 | ENV PORT=8080 \
5 | HOST=0.0.0.0 \
6 | FLASK_APP=/app/run.py \
7 | PYTHONUNBUFFERED=True
8 | ARG FLASK_DEBUG=False
9 | ENV FLASK_DEBUG=$FLASK_DEBUG
10 |
11 | # Setup file system
12 | WORKDIR /app
13 | COPY app/ /app
14 |
15 | # Upgrade pip & install python packages
16 | RUN pip install --upgrade pip --requirement /app/requirements.txt
17 |
18 | # Indicate which port to expose
19 | EXPOSE $PORT
20 |
21 | # Start app server
22 | CMD flask run --host=$HOST --port=$PORT
--------------------------------------------------------------------------------
/src/README.md:
--------------------------------------------------------------------------------
1 | # Sherlock
2 |
3 | Welcome to Sherlock project. Sherlock is a movie recommendation microservice written in Flask.
4 |
5 | Below steps can be executed on any unix like system. I will use ubuntu deployed on [O'Reilly's sandbox](https://learning.oreilly.com/scenarios/ubuntu-sandbox/9781492062837) (alternatively you could use [Katacoda's playground](https://www.katacoda.com/courses/ubuntu/playground2004)). Once the sandbox/playground is ready, execute instructions specified in below sections.
6 |
7 | ## Setup SSH key
8 |
9 | Create ssh key and add it to GitHub's [SSH keys](https://github.com/settings/keys) settings.
10 |
11 | ```bash
12 | ssh-keygen
13 | cat ~/.ssh/id_rsa.pub
14 | ```
15 |
16 | ## Installation
17 |
18 | ```bash
19 | # Cloning the source code
20 | git clone https://github.com/ldynia/flask-sherlock.git
21 | cd flask-sherlock
22 |
23 | # Building and running docker container
24 | docker build --tag flask-sherlock --build-arg FLASK_DEBUG=True .
25 | docker run --detach --name sherlock --publish 80:8080 --rm flask-sherlock
26 | docker ps
27 | ```
28 |
29 | ## API
30 |
31 | ```bash
32 | curl "http://localhost/api/v1/movies/recommend?title=Kingpin"
33 | curl "http://localhost/api/v1/movies/recommend?title=Lost%20in%20Translation"
34 | ```
35 |
36 | ## Testing
37 |
38 | Unit test
39 |
40 | ```bash
41 | docker exec sherlock pytest
42 | ```
43 |
44 | Code coverage
45 |
46 | ```bash
47 | docker exec sherlock coverage run -m pytest
48 | docker exec sherlock coverage report
49 | ```
50 |
51 | ## Debug
52 |
53 | ```bash
54 | {
55 | docker stop sherlock;
56 | docker build --no-cache --tag flask-sherlock $PWD;
57 | docker run --rm --detach --name sherlock --publish 80:8080 --volume $PWD/app:/app flask-sherlock;
58 | }
59 | ```
--------------------------------------------------------------------------------
/src/app/db.json:
--------------------------------------------------------------------------------
1 | [{
2 | "title": "Groundhog Day",
3 | "genre": ["comedy", "fantasy", "romance"],
4 | "year": 1993,
5 | "rating": 8.0,
6 | "directors": ["Harold Ramis"],
7 | "stars": ["Bill Murray", "Andie MacDowell", "Chris Elliott", "Punxsutawney Phil"]
8 | }, {
9 | "title": "Kingpin",
10 | "genre": ["comedy", "sport"],
11 | "year": 1996,
12 | "rating": 6.9,
13 | "director": ["Bobby Farrelly", "Peter Farrelly"],
14 | "stars": ["Woody Harrelson", "Randy Quaid", "Bill Murray"]
15 | }, {
16 | "title": "The Bridges of Madison County",
17 | "genre": ["drama", "romance"],
18 | "year": 1995,
19 | "rating": 7.6,
20 | "director": ["Clint Eastwood"],
21 | "stars": ["Clint Eastwood", "Meryl Streep"]
22 | }, {
23 | "title": "Good Will Hunting",
24 | "genre": ["drama", "romance"],
25 | "year": 1997,
26 | "rating": 8.3,
27 | "director": ["Gus Van Sant"],
28 | "stars": ["Robin Williams", "Matt Damon", "Ben Affleck"]
29 | }, {
30 | "title": "The Rainmaker",
31 | "genre": ["crime", "drama", "thriller"],
32 | "year": 1997,
33 | "rating": 7.2,
34 | "director": ["Francis Ford Coppola"],
35 | "stars": ["Matt Damon", "Danny DeVito", "Claire Danes"]
36 | }, {
37 | "title": "Ghost in the Shell",
38 | "genre": ["animation", "action", "crime"],
39 | "year": 1995,
40 | "rating": 8.0,
41 | "director": ["Mamoru Oshii"],
42 | "stars": ["Atsuko Tanaka", "Iemasa Kayumi", "Akio Ôtsuka"]
43 | }, {
44 | "title": "Aliens",
45 | "genre": ["action", "adventure", "sci-fi"],
46 | "year": 1986,
47 | "rating": 8.3,
48 | "director": ["James Cameron"],
49 | "stars": ["Sigourney Weaver", "Michael Biehn", "Carrie Henn"]
50 | }, {
51 | "title": "Terminator 2",
52 | "genre": ["action", "sci-fi"],
53 | "year": 1986,
54 | "rating": 8.5,
55 | "director": ["James Cameron"],
56 | "stars": ["Arnold Schwarzenegger", "Linda Hamilton", "Edward Furlong"]
57 | }, {
58 | "title": "Lethal Weapon 2",
59 | "genre": ["action", "crime", "thriller"],
60 | "year": 1989,
61 | "rating": 7.2,
62 | "director": ["Richard Donner"],
63 | "stars": ["Mel Gibson", "Danny Glover", "Joe Pesci"]
64 | }, {
65 | "title": "Lost in Translation",
66 | "genre": ["comedy", "drama"],
67 | "year": 3003,
68 | "rating": 7.7,
69 | "director": ["Sofia Coppola"],
70 | "stars": ["Bill Murray", "Scarlett Johansson", "Giovanni Ribisi"]
71 | }]
72 |
--------------------------------------------------------------------------------
/src/app/rengine.py:
--------------------------------------------------------------------------------
1 | import random
2 |
3 |
4 | class Sherlock():
5 | """
6 | Movies recommendation engine.
7 | """
8 |
9 | def __init__(self, movies, features):
10 | self.movies = movies
11 | self.title = features.get("title")
12 | self.features = ["genre", "stars"]
13 |
14 | def recommend(self):
15 | """
16 | Algorithm recommends movies based on default movie features.
17 | The algorithm uses partial match as search criteria and returns sorted list of movie(s).
18 | """
19 | ref_movie = self.__get_movie(self.title)
20 | if not ref_movie:
21 | return self.__lucky_recommendation(self.movies)
22 | ref_movie = ref_movie[0]
23 |
24 | movies = []
25 | for movie in self.movies:
26 | if movie["title"] != self.title:
27 | for feature in self.features:
28 | feature_match = [fm in movie[feature] for fm in ref_movie[feature]]
29 | if any(feature_match):
30 | movies.append(movie)
31 | break
32 |
33 | return sorted(movies, key=lambda movie: movie["rating"], reverse=True)
34 |
35 |
36 | def __lucky_recommendation(self, movies):
37 | """
38 | I feel lucky - random choice.
39 | """
40 | return [random.choice(movies)]
41 |
42 | def __get_movie(self, title):
43 | """
44 | Find movie by title.
45 | """
46 | movie = [movie for movie in self.movies if movie["title"] == title]
47 | return movie if movie else []
48 |
--------------------------------------------------------------------------------
/src/app/requirements.txt:
--------------------------------------------------------------------------------
1 | coverage
2 | Flask
3 | pytest
--------------------------------------------------------------------------------
/src/app/run.py:
--------------------------------------------------------------------------------
1 | import json
2 | from json.decoder import JSONDecodeError
3 |
4 | from flask import Flask
5 | from flask import jsonify
6 | from flask import request
7 |
8 | from rengine import Sherlock
9 |
10 |
11 | # Set up app
12 | app = Flask(__name__)
13 | app.config["JSON_AS_ASCII"] = False
14 |
15 |
16 | def read_data(source):
17 | """
18 | Reads file that is expected to hold JSON encoded content.
19 | In case of errors return empty data and list holding error message.
20 | """
21 | data = []
22 | errors = []
23 | try:
24 | with open(source) as db:
25 | content = db.read()
26 | data = json.loads(content)
27 | except FileNotFoundError as e:
28 | errors = [f"Reading {source}, {str(e)}"]
29 | except JSONDecodeError as e:
30 | errors = [f"Reading {source}, {str(e)}"]
31 | except Exception as e:
32 | errors = [f"Reading {source}, {str(e)}"]
33 |
34 | return data, errors
35 |
36 |
37 | @app.route("/api/v1/movies/recommend", methods=["GET"])
38 | def recommend():
39 | """
40 | Function loads movies from db and returns recommendations.
41 | """
42 | MOVIES, errors = read_data("/app/db.json")
43 | if errors:
44 | return jsonify({"errors": errors, "status_code": 500}), 500
45 |
46 | sherlock = Sherlock(MOVIES, request.args)
47 | recommendation = sherlock.recommend()
48 |
49 | return jsonify(recommendation)
50 |
--------------------------------------------------------------------------------
/src/app/test_app.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from run import app
4 |
5 |
6 | @pytest.fixture
7 | def client():
8 | app.config["TESTING"] = True
9 | with app.test_client() as client:
10 | yield client
11 |
12 |
13 | def test_api(client):
14 | response = client.get("/api/v1/movies/recommend")
15 | assert response.status_code == 200
16 | assert response.is_json
17 | assert response.get_json()[0]["title"] != ""
18 |
19 | response = client.get("/api/v1/movies/recommend?title=Kingpin")
20 | assert response.status_code == 200
21 | assert response.is_json
22 | assert len(response.get_json()) >= 2
23 |
24 | response = client.get("/api/v1/movies/recommend?title=Lost%20in%20Translation")
25 | assert response.status_code == 200
26 | assert response.is_json
27 | assert len(response.get_json()) >= 5
28 |
--------------------------------------------------------------------------------