Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

redockerize and deproxify #58

Draft
wants to merge 22 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
45e0a3f
Temporarily (?) disable post-generation setup of git, venv, db and no…
jgonggrijp Oct 30, 2024
4a27342
Generate frontend and backend dependency lists using Docker (#16)
jgonggrijp Oct 31, 2024
db6befc
DROPME temporarily disable subproject bootstrapping
jgonggrijp Nov 1, 2024
cd1fdbe
squash! Generate frontend and backend dependency lists using Docker (…
jgonggrijp Nov 1, 2024
ea08831
squash! Generate frontend and backend dependency lists using Docker (…
jgonggrijp Nov 1, 2024
db63f6f
squash! Generate frontend and backend dependency lists using Docker (…
jgonggrijp Nov 1, 2024
50e350c
Revert "DROPME temporarily disable subproject bootstrapping"
jgonggrijp Nov 1, 2024
8c43343
Move Angular shutil.move to end of function
jgonggrijp Nov 5, 2024
fe957db
Practice slash correctness in dockerfile
jgonggrijp Nov 5, 2024
d0b59f9
DROPME disable subproject initialization
jgonggrijp Nov 5, 2024
5fd98de
fixup hopefully indicate the files to copy more unambiguously
jgonggrijp Nov 5, 2024
53c658b
fixup give other copy same treatment
jgonggrijp Nov 5, 2024
e69f522
Revert "DROPME disable subproject initialization"
jgonggrijp Nov 5, 2024
5bb82f4
fixup indicate which stage of bootstrap is in progress
jgonggrijp Nov 5, 2024
e723a4c
DROPME disable most of angular activation
jgonggrijp Nov 5, 2024
a9cd3ef
DROPME disable file deletion as well
jgonggrijp Nov 5, 2024
aa299e3
Revert "DROPME disable file deletion as well"
jgonggrijp Dec 19, 2024
8b0b458
Revert "DROPME disable most of angular activation"
jgonggrijp Dec 19, 2024
a574ccd
Move all of the Angular activation into the Dockerfile-postgenerate (…
jgonggrijp Dec 19, 2024
05a9ca5
Clean up newly introduced post-generation files (#16)
jgonggrijp Dec 19, 2024
8f0089c
DROPME disable subproject init
jgonggrijp Dec 20, 2024
975fdb7
squash! Move all of the Angular activation into the Dockerfile-postge…
jgonggrijp Dec 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cookiecutter.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"project_title": "DigHum Pro",
"slug": "{{cookiecutter.project_title | lower | replace('-', '') | replace(' ', '_')}}",
"npm_slug": "{{cookiecutter.slug | replace('_', '-')}}",
"app_prefix": "dh",
"description": "{{cookiecutter.project_title}} will humanize all your digits!",
"author": "Research Software Lab, Centre for Digital Humanities, Utrecht University",
Expand Down
70 changes: 5 additions & 65 deletions hooks/post_gen_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,71 +35,6 @@ def python_path():

def main(argv):
print('\nFiles generated. Performing final steps.')
try:
activate_frontend()
except Exception as exception:
print(exception)
print("[ERROR] Activating frontend failed!!")
if '{{cookiecutter.frontend}}' == 'backbone' and not generate_backbone_translations(): return 1
venv = create_virtualenv()
pip_tools = backreq = backpack = clone_req = funcreq = funcpack = False
if venv:
adopt_virtualenv(VIRTUALENV)
pip_tools = install_pip_tools()
if pip_tools:
check_version_pip()
check_version_pip_compile()
backreq = compile_backend_requirements()
if backreq:
backpack = install_backend_packages()
clone_req = copy_backreq_to_func()
if clone_req:
funcreq = compile_functest_requirements()
if funcreq:
funcpack = install_functest_packages()
frontpack = install_frontend_packages()
git = setup_git()
gitflow = develop = stage = commit = remote = False
if git:
gitflow = setup_gitflow()
if not gitflow:
develop = create_develop_branch()
if funcreq and frontpack:
stage = git_add()
if stage:
commit = initial_commit()
remote = add_remote()
db = create_db()
if db:
db = grant_db()
migrate = superuser = False
if db and backpack:
migrate = run_migrations()
if migrate:
superuser = create_superuser()
if not all([superuser, remote, commit, gitflow, funcpack]):
print('\nPlease read {} for information on failed commands.'.format(LOGFILE_NAME))
print('\nAlmost ready to go! Just a couple more commands to run:')
print(cd_into_project)
if not venv: print(create_virtualenv)
print(activate_venv)
if not pip_tools: print(install_pip_tools)
if not backreq: print(compile_backend_requirements)
if not clone_req: print(copy_backreq_to_func)
if not funcreq: print(compile_functest_requirements)
if not (backpack and frontpack and funcpack): print(install_all_packages)
if not git: print(setup_git)
if not gitflow and not develop: print(create_develop_branch)
if not stage: print(git_add)
if not commit: print(initial_commit)
if not remote: print(add_remote)
if not db:
print(create_db)
print(grant_db)
if not migrate: print(run_migrations)
if not superuser: print(create_superuser)
print(git_push)
print(yarn_start)
Comment on lines -44 to -102
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some, but not all, of this logic is or will be restored via the bootstrap_subprojects command, which relies on docker-compose.



def generate_backbone_translations():
Expand All @@ -121,6 +56,11 @@ def generate_backbone_translations():
return True


bootstrap_subprojects = Command(
'Finalize subproject package configuration',
['docker', 'compose', '-f', 'compose-postgenerate.yml', 'up']
)

cd_into_project = Command('', ['cd', SLUG])

create_virtualenv = make_create_venv_command(VIRTUALENV_COMMAND)
Expand Down
7 changes: 7 additions & 0 deletions {{cookiecutter.slug}}/backend/Dockerfile-postgenerate
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM python:3.9

WORKDIR /usr/src/app/backend
COPY requirements.in .
RUN pip install -U pip pip-tools

CMD pip-compile
91 changes: 2 additions & 89 deletions {{cookiecutter.slug}}/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,110 +191,23 @@ def make_access_db_command(psql_cmd):
)


def merge_json(target, source):
for key, value in source.items():
if value is None:
del target[key]
elif key in target and isinstance(target[key], dict) and \
isinstance(source[key], dict):
merge_json(target[key], source[key])
else:
target[key] = value
return target


def activate_frontend():
framework = '{{cookiecutter.frontend}}'
os.rename('package.{{cookiecutter.frontend}}.json', 'package.json')

if framework == 'backbone':
os.rename('frontend.backbone', 'frontend')
shutil.move(op.join('frontend', 'proxy.json'), 'proxy.json')
override_json('package')
elif framework == 'angular':
project_name = '{{cookiecutter.slug}}'.replace('_', '-')
Command(
'Install dependencies',
['yarn', 'install', '--ignore-scripts']
)()
Command(
'Creating project',
['yarn', 'ng', 'new', project_name, '--prefix={{cookiecutter.app_prefix}}',
'--ssr',
'--skip-git=true',
'--skip-install=true',
'--package-manager=yarn',
'--style=scss',
'--routing=true']
)()
shutil.copytree('frontend.angular', project_name, dirs_exist_ok=True)
os.rename(project_name, 'frontend')
shutil.move(op.join('frontend', 'proxy.conf.json'), 'proxy.conf.json')
override_json('package')
Command(
'Install frontend dependencies using Yarn',
['yarn'],
cwd="frontend"
)()
# Remove favicon.ico
os.remove(os.path.join('frontend', 'src', 'favicon.ico'))
# Remove editorconfig
os.remove(os.path.join('frontend', '.editorconfig'))
Command(
'ng add @angular/localize',
['yarn', 'ng', 'add', '@angular/localize', '--skip-confirmation'],
cwd="frontend"
)()
Comment on lines -234 to -247
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is implemented in the bootstrap-2 stage of the frontend.angular/Dockerfile-postgenerate. It doesn't work as intended for a surprising reason, which I'll highlight in the Dockerfile.


override_json('angular')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is retained, but appears as deleted in the current diff because I outcommented it in debugging.

Command(
'Creating localizations',
['yarn', 'i18n'],
cwd="frontend"
)()
Comment on lines -250 to -254
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is implemented in the bootstrap-3 stage of the frontend.angular/Dockerfile-postgenerate. I will probably need to debug this as well after we manage to get stage 2 to work.

for lang in '{{cookiecutter.localizations}}'.split(','):
[code, lang_name] = lang.split(':')
with open(f'frontend/locale/messages.xlf', 'r') as file:
messages = file.read()
if code != '{{cookiecutter.default_localization}}':
with open(f'frontend/locale/messages.{code}.xlf', 'w') as file:
# add the target-language attribute after the source-language attribute
targeted = re.sub(r'(source-language="[^"]+"[^>]*)', f'\\g<1> target-language="{code}"', messages)
try:
with open(f'frontend/locale/messages.{code}.json', 'r') as pretranslated:
translations = json.load(pretranslated)
for key, value in translations.items():
targeted = targeted.replace(f'<source>{key}</source>', f'<source>{key}</source>\n <target state="translated">{value}</target>')
os.remove(f'frontend/locale/messages.{code}.json')
except FileNotFoundError:
pass
file.write(targeted)
Comment on lines -255 to -271
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is retained, but currently appears deleted because I outcommented it.

if '{{cookiecutter.frontend_port}}' != '4200':
Command(
'Set frontend port',
['yarn', 'ng', 'config', "projects.{{cookiecutter.slug | replace('_', '-')}}.architect.serve.options.port", '{{cookiecutter.frontend_port}}'],
cwd="frontend"
)()
Comment on lines -272 to -277
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I plan to just drop this. Frontend port customization should no longer be necessary with the finished Docker setup.

else:
print('Unknown framework {{cookiecutter.frontend}} specified!')
# remove other frameworks
for path in glob.glob("frontend.*"):
shutil.rmtree(path)
for path in glob.glob("package.*.json"):
os.remove(path)


def override_json(filename):
if os.path.isfile(f'frontend/{filename}.overwrite.json'):
print(f'Overriding {filename}.json')
with open(f'frontend/{filename}.overwrite.json', 'r') as file:
overwrite = json.load(file)
with open(f'frontend/{filename}.json', 'r') as file:
data = json.load(file)
with open(f'frontend/{filename}.json', 'w') as file:
merge_json(data, overwrite)
json.dump(data, file, indent=4)
os.remove(f'frontend/{filename}.overwrite.json')
os.remove(op.join('frontend', 'Dockerfile-postgenerate'))
shutil.rmtree(op.join('frontend', 'bootstrap'))


install_pip_tools = Command(
Expand Down
17 changes: 17 additions & 0 deletions {{cookiecutter.slug}}/compose-postgenerate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
services:
backend:
build:
context: ./backend
dockerfile: Dockerfile-postgenerate
volumes:
- ./backend:/usr/src/app/backend
frontend:
build:
context: ./frontend.{{cookiecutter.frontend}}
dockerfile: Dockerfile-postgenerate
volumes:
- ./frontend:/usr/src/app/frontend
- frnomo:/usr/src/app/frontend/node_modules

volumes:
frnomo:
47 changes: 47 additions & 0 deletions {{cookiecutter.slug}}/frontend.angular/Dockerfile-postgenerate
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
FROM node:18 AS bootstrap-1

WORKDIR /usr/src/app
RUN yarn init -yp && \
yarn add "@angular/cli@17" && \
yarn ng new {{cookiecutter.npm_slug}} \
--prefix={{cookiecutter.app_prefix}} \
--ssr \
--skip-git=true \
--skip-install=true \
--package-manager=yarn \
--style=scss \
--routing=true

WORKDIR /usr/src/app/{{cookiecutter.npm_slug}}
COPY . .

FROM python:3.9 AS bootstrap-2

WORKDIR /usr/src/app
COPY --from=bootstrap-1 \
/usr/src/app/{{cookiecutter.npm_slug}} frontend/

WORKDIR /usr/src/app/frontend
RUN python bootstrap/override_json.py package

FROM node:18 AS bootstrap-3

WORKDIR /usr/src/app/frontend
RUN rm src/favicon.ico .editorconfig && \
yarn && \
yarn ng add @angular/localize --skip-confirmation

FROM python:3.9 AS bootstrap-4

WORKDIR /usr/src/app/frontend
RUN python bootstrap/override_json.py angular

FROM node:18 AS bootstrap-5

WORKDIR /usr/src/app/frontend
RUN yarn ng extract-i18n --output-path locale

FROM python:3.9 AS bootstrap-6

WORKDIR /usr/src/app/frontend
RUN python bootstrap/finish_localizations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import re
import json
import os


def main():
for lang in '{{cookiecutter.localizations}}'.split(','):
[code, lang_name] = lang.split(':')
with open(f'locale/messages.xlf', 'r') as file:
messages = file.read()
if code != '{{cookiecutter.default_localization}}':
with open(f'locale/messages.{code}.xlf', 'w') as file:
# add the target-language attribute after the source-language attribute
targeted = re.sub(r'(source-language="[^"]+"[^>]*)', f'\\g<1> target-language="{code}"', messages)
try:
with open(f'locale/messages.{code}.json', 'r') as pretranslated:
translations = json.load(pretranslated)
for key, value in translations.items():
targeted = targeted.replace(f'<source>{key}</source>', f'<source>{key}</source>\n <target state="translated">{value}</target>')
os.remove(f'locale/messages.{code}.json')
except FileNotFoundError:
pass
file.write(targeted)


if __name__ == '__main__':
main()
31 changes: 31 additions & 0 deletions {{cookiecutter.slug}}/frontend.angular/bootstrap/override_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import os
import sys


def merge_json(target, source):
for key, value in source.items():
if value is None:
del target[key]
elif key in target and isinstance(target[key], dict) and \
isinstance(source[key], dict):
merge_json(target[key], source[key])
else:
target[key] = value
return target


def override_json(filename):
if os.path.isfile(f'{filename}.overwrite.json'):
print(f'Overriding {filename}.json')
with open(f'{filename}.overwrite.json', 'r') as file:
overwrite = json.load(file)
with open(f'{filename}.json', 'r') as file:
data = json.load(file)
with open(f'{filename}.json', 'w') as file:
merge_json(data, overwrite)
json.dump(data, file, indent=4)
os.remove(f'{filename}.overwrite.json')


if __name__ == '__main__':
override_json(sys.argv[1])
Loading