Skip to content

Commit 9029e9f

Browse files
authored
Merge pull request #502 from sir-gon/feature/production_ready
Feature/production ready
2 parents ff4ce76 + c2af05a commit 9029e9f

File tree

65 files changed

+548
-488
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+548
-488
lines changed

.github/workflows/docker-image.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ jobs:
1717
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
1818
- name: Build the Docker image
1919
run: make compose/rebuild
20-
- name: Run static checks in Docker image
20+
- name: Lint in Docker image
2121
run: make compose/lint
22-
- name: Run test in Docker image
22+
- name: Test in Docker image
23+
run: make compose/test
24+
- name: Run in Docker image
2325
run: make compose/run
2426
- name: Tag Docker image
2527
run: >

Dockerfile

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ FROM python:3.12.4-alpine3.20 AS base
44
ENV WORKDIR=/app
55
WORKDIR ${WORKDIR}
66

7+
RUN apk add --update --no-cache make
8+
79
###############################################################################
810
FROM base AS lint
911

@@ -35,34 +37,51 @@ COPY ./requirements.txt ${WORKDIR}/
3537
COPY ./setup.cfg ${WORKDIR}/
3638
COPY ./Makefile ${WORKDIR}/
3739

40+
# code linting conf
41+
COPY ./.pylintrc ${WORKDIR}/
42+
COPY ./.coveragerc ${WORKDIR}/
43+
COPY ./setup.cfg ${WORKDIR}/
44+
3845
# markdownlint conf
3946
COPY ./.markdownlint.yaml ${WORKDIR}/
4047

4148
# yamllint conf
4249
COPY ./.yamllint ${WORKDIR}/
4350
COPY ./.yamlignore ${WORKDIR}/
4451

45-
# pylint and covergae
46-
COPY ./.pylintrc ${WORKDIR}/
47-
COPY ./.coveragerc ${WORKDIR}/
48-
4952
CMD ["make", "lint"]
5053

5154
###############################################################################
5255
FROM base AS development
5356

54-
RUN apk add --update --no-cache make
57+
COPY ./Makefile ${WORKDIR}/
58+
COPY ./requirements.txt ${WORKDIR}/
59+
COPY ./setup.cfg ${WORKDIR}/
5560

56-
###############################################################################
57-
FROM development AS builder
61+
RUN make dependencies
5862

5963
COPY ./src ${WORKDIR}/src
60-
COPY ./requirements.txt ${WORKDIR}/
61-
COPY ./Makefile ${WORKDIR}/
62-
COPY ./setup.cfg ${WORKDIR}/
64+
6365
RUN ls -alh
6466

65-
RUN pip install -r requirements.txt
67+
# CMD []
68+
69+
###############################################################################
70+
FROM development AS builder
71+
72+
ENV WORKDIR=/app
73+
WORKDIR ${WORKDIR}
74+
75+
RUN apk add --update --no-cache rsync
76+
77+
RUN rsync -av --prune-empty-dirs \
78+
--exclude '*_test.py' \
79+
--exclude '*.pyc' \
80+
--exclude '.venv' \
81+
--exclude '__pycache__' \
82+
src/ build/
83+
84+
# CMD []
6685

6786
###############################################################################
6887
### In testing stage, can't use USER, due permissions issue
@@ -78,27 +97,36 @@ ENV BRUTEFORCE=false
7897
WORKDIR /app
7998

8099
COPY ./.coveragerc ${WORKDIR}/
100+
COPY ./setup.cfg ${WORKDIR}/
101+
81102
RUN ls -alh
82103

83-
CMD ["make", "test", "-e", "{DEBUG}"]
104+
CMD ["make", "test"]
84105

85106
###############################################################################
86107
### In production stage
87108
## in the production phase, "good practices" such as
88109
## WORKDIR and USER are maintained
89110
##
90-
FROM builder AS production
111+
FROM python:3.12.4-alpine3.20 AS production
91112

92113
ENV LOG_LEVEL=INFO
93114
ENV BRUTEFORCE=false
115+
ENV WORKDIR=/app
116+
WORKDIR ${WORKDIR}
94117

95118
RUN adduser -D worker
96119
RUN mkdir -p /app
97120
RUN chown worker:worker /app
98121

99-
WORKDIR /app
122+
RUN apk add --update --no-cache make
123+
COPY ./Makefile ${WORKDIR}/
124+
125+
COPY --from=builder /app/build/ ${WORKDIR}/
100126

101127
RUN ls -alh
102128

103129
USER worker
104-
CMD ["make", "test", "-e", "{DEBUG}"]
130+
CMD ["make", "run"]
131+
132+
# checkov:skip= CKV_DOCKER_2: production image isn't a service process (yet)

Makefile

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,17 @@ test/static: dependencies
8181
test/styling: dependencies
8282
${RUNTIME_TOOL} -m pycodestyle --statistics src/
8383

84+
format:
85+
${RUNTIME_TOOL} -m autopep8 --in-place --recursive --aggressive --aggressive --verbose src/
86+
87+
build: env
88+
rsync -av --prune-empty-dirs \
89+
--exclude '*_test.py' \
90+
--exclude '*.pyc' \
91+
--exclude '.venv' \
92+
--exclude '__pycache__' \
93+
src/ build/
94+
8495
test: env dependencies
8596
${RUNTIME_TOOL} -m coverage run -m \
8697
pytest --verbose \
@@ -110,17 +121,20 @@ clean:
110121
rm -fr .pytest_cache
111122
rm -fr htmlcov
112123
rm -fr coverage
124+
rm -fr build
113125
find . -path "*/*.pyc" -delete -print
114126
find . -path "*/*.pyo" -delete -print
115127
find . -path "*/__pycache__" -type d -print -exec rm -fr {} ';'
116128

117129
compose/build: env
118130
docker-compose --profile lint build
119131
docker-compose --profile testing build
132+
docker-compose --profile production build
120133

121134
compose/rebuild: env
122135
docker-compose --profile lint build --no-cache
123136
docker-compose --profile testing build --no-cache
137+
docker-compose --profile production build --no-cache
124138

125139
compose/lint/markdown: compose/build
126140
docker-compose --profile lint run --rm algorithm-exercises-py-lint make lint/markdown
@@ -136,7 +150,13 @@ compose/test/static: compose/build
136150

137151
compose/lint: compose/lint/markdown compose/lint/yaml compose/test/styling compose/test/static
138152

153+
compose/test: compose/build
154+
docker-compose --profile testing run --rm algorithm-exercises-py-test make test
155+
139156
compose/run: compose/build
140-
docker-compose --profile testing run --rm algorithm-exercises-py make test
157+
docker-compose --profile production run --rm algorithm-exercises-py make run
141158

142159
all: lint coverage
160+
161+
run:
162+
ls -alh

compose.yaml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
---
22

33
services:
4-
algorithm-exercises-py:
5-
image: algorithm-exercises-py:latest
4+
algorithm-exercises-py-test:
5+
image: algorithm-exercises-py:test
66
build:
77
context: .
88
target: testing
@@ -37,6 +37,16 @@ services:
3737
- ./:/app
3838
profiles: ["development"]
3939

40+
algorithm-exercises-py:
41+
image: algorithm-exercises-py:latest
42+
build:
43+
context: .
44+
target: production
45+
environment:
46+
LOG_LEVEL: ${LOG_LEVEL:-INFO} ## (1) ## INFO | DEBUG
47+
BRUTEFORCE: ${BRUTEFORCE:-false} ## (1) ## true | false
48+
profiles: ["production"]
49+
4050
## REFERENCES:
4151
## (1) Passing Environment variable with fallback value:
4252
## https://stackoverflow.com/a/70772707/6366150

src/hackerrank/interview_preparation_kit/arrays/cruch_bruteforce.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def array_manipulation(n: int, queries: list[list[int]]) -> int:
1414
for [a, b, k] in queries:
1515

1616
LOGGER.debug("start -> %s", result)
17-
for j in range(a, b+1):
17+
for j in range(a, b + 1):
1818
result[j] += k
1919
LOGGER.debug("result -> %s", result)
2020

src/hackerrank/interview_preparation_kit/arrays/cruch_optimized.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def array_manipulation_optimized(n: int, queries: list[list[int]]) -> int:
1919
for [a, b, k] in queries:
2020
# Prefix
2121
result[a] += k
22-
result[b+1] -= k
22+
result[b + 1] -= k
2323

2424
accum_sum = 0
2525
for value in result:
Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
CRUCH_SMALL_TEST_CASES = tests = [
2-
{
3-
'title': "Sample Test Case 0",
4-
'n': 5,
5-
'queries': [[1, 2, 100],
6-
[2, 5, 100],
7-
[3, 4, 100]],
8-
'answer': 200
9-
},
10-
{
11-
'title': "Sample Test Case 1",
12-
'n': 10,
13-
'queries': [[1, 5, 3],
14-
[4, 8, 7],
15-
[6, 9, 1]],
16-
'answer': 10
17-
},
18-
{
19-
'title': "Sample Test Case 3",
20-
'n': 10,
21-
'queries': [[2, 6, 8],
22-
[3, 5, 7],
23-
[1, 8, 1],
24-
[5, 9, 15]],
25-
'answer': 31
26-
},
27-
]
2+
{
3+
'title': "Sample Test Case 0",
4+
'n': 5,
5+
'queries': [[1, 2, 100],
6+
[2, 5, 100],
7+
[3, 4, 100]],
8+
'answer': 200
9+
},
10+
{
11+
'title': "Sample Test Case 1",
12+
'n': 10,
13+
'queries': [[1, 5, 3],
14+
[4, 8, 7],
15+
[6, 9, 1]],
16+
'answer': 10
17+
},
18+
{
19+
'title': "Sample Test Case 3",
20+
'n': 10,
21+
'queries': [[2, 6, 8],
22+
[3, 5, 7],
23+
[1, 8, 1],
24+
[5, 9, 15]],
25+
'answer': 31
26+
},
27+
]

src/hackerrank/interview_preparation_kit/arrays/ctci_array_left_rotation_test.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ class TestArrayLeftRotation(unittest.TestCase):
77
def test_rot_left_one(self):
88

99
tests = [
10-
{'input': [1, 2, 3, 4, 5], 'answer': [2, 3, 4, 5, 1]},
11-
{'input': [2, 3, 4, 5, 1], 'answer': [3, 4, 5, 1, 2]},
12-
{'input': [3, 4, 5, 1, 2], 'answer': [4, 5, 1, 2, 3]},
13-
{'input': [4, 5, 1, 2, 3], 'answer': [5, 1, 2, 3, 4]},
14-
{'input': [5, 1, 2, 3, 4], 'answer': [1, 2, 3, 4, 5]},
10+
{'input': [1, 2, 3, 4, 5], 'answer': [2, 3, 4, 5, 1]},
11+
{'input': [2, 3, 4, 5, 1], 'answer': [3, 4, 5, 1, 2]},
12+
{'input': [3, 4, 5, 1, 2], 'answer': [4, 5, 1, 2, 3]},
13+
{'input': [4, 5, 1, 2, 3], 'answer': [5, 1, 2, 3, 4]},
14+
{'input': [5, 1, 2, 3, 4], 'answer': [1, 2, 3, 4, 5]},
1515
]
1616

1717
for _, _tt in enumerate(tests):
@@ -24,7 +24,7 @@ def test_rot_left_one(self):
2424
def test_rot_left(self):
2525

2626
tests = [
27-
{'input': [1, 2, 3, 4, 5], 'd': 4, 'answer': [5, 1, 2, 3, 4]},
27+
{'input': [1, 2, 3, 4, 5], 'd': 4, 'answer': [5, 1, 2, 3, 4]},
2828
]
2929

3030
for _, _tt in enumerate(tests):

src/hackerrank/interview_preparation_kit/arrays/minimum_swaps_2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88

99
def minimum_swaps(group: list[int]) -> int:
10-
q = [i-1 for i in group]
10+
q = [i - 1 for i in group]
1111

1212
swaps = 0
1313
index = 0

src/hackerrank/interview_preparation_kit/arrays/minimum_swaps_2_test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ class TestMinimumSwaps(unittest.TestCase):
77
def test_minimum_swaps(self):
88

99
tests = [
10-
{'title': 'Sample input 0', 'input': [4, 3, 1, 2], 'answer': 3},
11-
{'title': 'Sample input 1', 'input': [2, 3, 4, 1, 5], 'answer': 3},
12-
{'title': 'Sample input 2', 'input': [1, 3, 5, 2, 4, 6, 7], 'answer': 3},
10+
{'title': 'Sample input 0', 'input': [4, 3, 1, 2], 'answer': 3},
11+
{'title': 'Sample input 1', 'input': [2, 3, 4, 1, 5], 'answer': 3},
12+
{'title': 'Sample input 2', 'input': [1, 3, 5, 2, 4, 6, 7], 'answer': 3},
1313
]
1414

1515
for _, _tt in enumerate(tests):

src/hackerrank/interview_preparation_kit/arrays/new_year_chaos_test.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ def test_minimum_bribes(self):
88

99
tests = [
1010
{'title': "Test Case 0-0",
11-
'input': [2, 1, 5, 3, 4], 'answer': 3},
11+
'input': [2, 1, 5, 3, 4], 'answer': 3},
1212
{'title': "Test Case 0-1",
13-
'input': [2, 5, 1, 3, 4], 'answer': TOO_CHAOTIC_ERROR},
13+
'input': [2, 5, 1, 3, 4], 'answer': TOO_CHAOTIC_ERROR},
1414
{'title': "Test Case 1-1",
15-
'input': [5, 1, 2, 3, 7, 8, 6, 4], 'answer': TOO_CHAOTIC_ERROR},
15+
'input': [5, 1, 2, 3, 7, 8, 6, 4], 'answer': TOO_CHAOTIC_ERROR},
1616
{'title': "Test Case 1-2",
17-
'input': [1, 2, 5, 3, 7, 8, 6, 4], 'answer': 7},
17+
'input': [1, 2, 5, 3, 7, 8, 6, 4], 'answer': 7},
1818
{'title': "Test Case 2",
19-
'input': [1, 2, 5, 3, 4, 7, 8, 6], 'answer': 4},
19+
'input': [1, 2, 5, 3, 4, 7, 8, 6], 'answer': 4},
2020
]
2121

2222
for _, _tt in enumerate(tests):

src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/count_triplets_1.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def count_triplets_brute_force(arr: list[int], r: int) -> int:
88

99
for i in range(0, size - 2):
1010
for j in range(i + 1, size - 1):
11-
for k in range(j+1, size):
11+
for k in range(j + 1, size):
1212
print(arr[i], arr[j], arr[k])
1313

1414
if r * arr[i] == arr[j] and r * arr[j] == arr[k]:
@@ -23,8 +23,8 @@ def count_triplets(arr, r):
2323
triplets = 0
2424

2525
for i in arr:
26-
j = i//r
27-
k = i*r
26+
j = i // r
27+
k = i * r
2828
a[i] -= 1
2929
if b[j] and a[k] and i % r == 0:
3030
triplets += b[j] * a[k]

src/hackerrank/interview_preparation_kit/dynamic_programming/max_array_sum.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def max_array_sum(arr_input: list[int]):
1818
arr[1] = t_max
1919

2020
for i in range(2, n):
21-
t_max = max(arr[i-2] + arr[i], t_max) # Max uptill now
21+
t_max = max(arr[i - 2] + arr[i], t_max) # Max uptill now
2222
t_max = max(arr[i], t_max) # Max in special case where
2323
# arr[i] + previous max is still less than arr[i]
2424
# update our inplace array with max for future calculations

src/hackerrank/interview_preparation_kit/graphs/roads_and_libraries.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class RoadsAndLibraries:
99

1010
def __init__(self, n: int, cities: list[list[int]]):
1111

12-
self._paths = [-1 for _ in range(n+1)]
12+
self._paths = [-1 for _ in range(n + 1)]
1313

1414
for path in cities:
1515
a, b = path[0], path[1]

src/hackerrank/interview_preparation_kit/greedy_algorithms/angry_children_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
{
1212
'title': 'Sample Test case 1',
1313
'k': 4,
14-
'arr': [1, 2, 3, 4, 10, 20, 30, 40, 100, 200],
14+
'arr': [1, 2, 3, 4, 10, 20, 30, 40, 100, 200],
1515
'answer': 3
1616
},
1717
{

0 commit comments

Comments
 (0)