From 8a08605a548cd76f0041908f1d8f69a8fbf49ee0 Mon Sep 17 00:00:00 2001 From: Wesley B <62723358+wesleyboar@users.noreply.github.com> Date: Mon, 26 Jun 2023 13:57:02 -0500 Subject: [PATCH] feat: rewrite README, cleanup non-APCD projects (#172) --- README.md | 150 ++++++++++++++---- demdata_cms/docker-compose.dev.yml | 4 +- .../src/taccsite_cms/secrets.default.py | 19 --- .../src/taccsite_cms/settings_custom.py | 4 - docs/port-project.md | 79 +++++++++ docs/run-project.md | 51 ++++++ example_cms/docker-compose.dev.yml | 26 +-- .../src/taccsite_cms/secrets.default.py | 19 --- .../{matcssi_cms => example_cms}/__init__.py | 0 .../example_cms}/img/favicons/favicon.ico | Bin .../example_cms/img/org_logos/portal.png | Bin 0 -> 1913 bytes .../matcssi_cms/img/org_logos/matcssi.png | Bin 14074 -> 0 bytes matcssi_cms/docker-compose.dev.yml | 26 +-- .../src/taccsite_cms/secrets.default.py | 19 --- 14 files changed, 281 insertions(+), 116 deletions(-) delete mode 100644 demdata_cms/src/taccsite_cms/secrets.default.py create mode 100644 docs/port-project.md create mode 100644 docs/run-project.md delete mode 100644 example_cms/src/taccsite_cms/secrets.default.py rename example_cms/src/taccsite_custom/{matcssi_cms => example_cms}/__init__.py (100%) rename example_cms/src/taccsite_custom/{matcssi_cms/static/matcssi_cms => example_cms/static/example_cms}/img/favicons/favicon.ico (100%) create mode 100644 example_cms/src/taccsite_custom/example_cms/static/example_cms/img/org_logos/portal.png delete mode 100644 example_cms/src/taccsite_custom/matcssi_cms/static/matcssi_cms/img/org_logos/matcssi.png delete mode 100644 matcssi_cms/src/taccsite_cms/secrets.default.py diff --git a/README.md b/README.md index 13aa37c9..3fe2ee25 100644 --- a/README.md +++ b/README.md @@ -2,63 +2,155 @@ Extensions of the [Core CMS] project +## Table of Contents + +- [Related Repositories](#related-repositories) +- [Project Architecture](#project-architecture) +- [Start Project](#start-project) +- [Update Project](#update-project) +- [Develop Project](#develop-project) +- [Build Project](#build-project) +- [Port Project](#port-project) + ## Related Repositories - [Camino], a Docker container-based deployment scheme +- [Core CMS], the base CMS code for TACC WMA CMS Websites - [Core Portal], the base Portal code for TACC WMA CMS Websites +- [Core Styles], the shared UI pattern code for TACC WMA CMS Websites - [Core CMS Resources], the original solution for extensions of the [Core CMS] project - [Core Portal Deployments], private repository that facilitates deployments of [Core Portal] images via [Camino] and Jenkins -## Architecture +## Project Architecture + +Within a `/custom_project_dir` can be: + +| directory | contents | +| - | - | +| `src/apps` | additional Django applications | +| `src/taccsite_cms` | settings for [Core CMS], additional apps, static assets, or middleware | +| `src/taccsite_custom` | templates and static assets, organized as Django CMS expects | + +## Prerequisites + +- [Docker] ≥ v20 +- [Docker Compose] ≥ v1 +- [Python] ≥ v3.8 + +A CMS project is run using [Docker][1] and [Docker Compose][2]. Both must be pre-installed on the system on which you will run the CMS. + +[^2]: On a Mac or a Windows machine, we recommended you install +[Docker Desktop](https://www.docker.com/products/docker-desktop), which will install both Docker and Docker Compose as well as Docker Machine, which is required to run Docker on Mac/Windows hosts. -- [`.../src/apps`](./src/apps/): Contains any additional Django applications -- [`.../src/taccsite_cms`](./src/taccsite_cms/): Contains settings files which specify additional apps, static files and middleware to load on top of Core CMS, along with standard Core CMS settings files -- [`.../src/taccsite_custom`](./src/taccsite_custom/): Contains static assets and templates, organized in the way that Django CMS expects them before imported via `python manage.py collectstatic`. +> **Notes** +> See [Core-CMS](https://github.com/TACC/Core-CMS) to verify the latest prerequisites. -## Local Development Setup +## Start Project -### Prequisites for Running the CMS +Set up a new local CMS instance. -See [Core-CMS](https://github.com/TACC/Core-CMS#prequisites-for-running-the-cms) "Prequisites for Running the CMS" section. +0. Python Application: -### Code Configuration + In the `custom_project_dir/` you will run, create a `settings_local.py` with content from [Core-CMS `settings_local.example.py`](https://github.com/TACC/Core-CMS/blob/main/taccsite_cms/settings_local.example.py). -#### Settings +1. Docker Containers: -1. Understand how settings are organized. See [Core-CMS](https://github.com/TACC/Core-CMS#settings) "Settings" section. -2. **If** the project you work on has a `taccsite_cms/secrets.default.py`, **then**: + ```sh + cd custom_project_dir + make start + ``` + + ```sh + docker exec -it core_cms /bin/bash + # This opens a command prompt within the container. + ``` + +2. Django Application: - ```bash - cd custom-project-dir - cp taccsite_cms/secrets.default.py taccsite_cms/secrets.py + (Run these commands within the container.) + + ```sh + python manage.py migrate + python manage.py createsuperuser + # To use default "Username" and skip "Email address", press Enter at both prompts. + # At "Password" prompts, you may use an insecure easy-to-remember password. + python manage.py collectstatic --no-input + # If the project has no static assets, + # (and you have not set up other projects) + # then expect output of "0 static files […]" ``` -3. Create a `settings_local.py` with content from [Core-CMS `settings_local.example.py`](https://github.com/TACC/Core-CMS/blob/main/taccsite_cms/settings_local.example.py). -## Running the CMS +3. Django CMS: + 1. Open http://0.0.0.0:8000/. + 2. Login with the credentials from step 2. + 3. Create one CMS page. (With "New page" highlighted, click "Next" button.) + - This page will automatically be your local homepage. + +> **Note** +> A local machine CMS will be empty. It will **not** have content from staging nor production. To have that, follow and adapt instructions to [copy a database](https://confluence.tacc.utexas.edu/x/W4DZDg). -### Running in Development Mode +> **Note** +> A local machine CMS does **not** include **nor** integrate with an instance of [Core Portal]. There are no reliable instructions to do either. **Help welcome.** -A `Makefile` has been included for convenience in each CMS project. You may use: +## Update Project -```bash -cd custom-project-dir -make start -``` +Update an existing local CMS instance. -### Running Custom App on a Custom CMS +1. If CMS Docker image changed, rebuild Docker Containers: + + ```sh + cd custom_project_dir + make stop + make build + make start + ``` + +2. If static assets or database models changed[^1], update the Django Application: + + ```sh + docker exec -it core_cms /bin/bash + ``` + + (Run these commands within the container.) + + ```sh + python manage.py migrate + python manage.py collectstatic --no-input + ``` + +[^1]: Pertinent changes are those in the Core CMS or the custom project. Changes to external assets or databases are not pertinent. + +## Run Project + +Read the relevant `custom_project_dir/README.md`. + +To run multiple projects, first read [Multiple Projects](docs/run-projects.md#multiple-projects). + +## Develop Project + +### Running a Custom App - Update `custom_app_settings.py` with relevant content from [TACC/Core-CMS:`/taccsite_cms/custom_app_settings.example.py`](https://github.com/TACC/Core-CMS/blob/1d88c35/taccsite_cms/custom_app_settings.example.py). - Update `urls_custom.py` with relevant content from [TACC/Core-CMS:`/taccsite_cms/urls_custom.example.py`](https://github.com/TACC/Core-CMS/blob/1d88c35/taccsite_cms/urls_custom.example.py). -## Porting from [Core CMS Resources] +## Build Project -When porting a downstream CMS project from [Core CMS Resources], the contents of a specific project's custom assets should be copied to [`./custom_project-dir/src/taccsite_custom`](./src/taccsite_custom/). The `settings_custom.py` from the CMS project directory should be moved to [`./custom-project-dir/src/taccsite_cms`](./src/taccsite_cms/) since that is where the file would be placed during a CMS build process from Jenkins. +Builds result in images that can be deployed. A build alone is not a deploy. +| Automatic Build | Manual Build | +| - | - | +| Occurs for each custom project directory (e.g. `demdata_cms`) upon each push to `main`. | Follow [GitHub Docs: GitHub Actions: Running a Workflow](https://docs.github.com/en/actions/using-workflows/manually-running-a-workflow#running-a-workflow). | -## Automatic Builds +> **Note** +> To check status of any build, see [Actions](https://github.com/TACC/Core-CMS-Custom/actions). -Automatic builds (not deploys) should occur on pushes to each CMS directory e.g. `apcd-cms`. +## Deploy Project +Follow "Core-CMS-Custom" section of [How To Build & Deploy](https://confluence.tacc.utexas.edu/x/Lo99E). + +## Port Project + +To port a project from another repository (e.g. [Core CMS Resources]), read [Port Projects](docs/port-projects.md). @@ -68,3 +160,7 @@ Automatic builds (not deploys) should occur on pushes to each CMS directory e.g. [Core Styles]: https://github.com/TACC/tup-ui/tree/main/libs/core-styles [Core CMS Resources]: https://github.com/TACC/Core-CMS-Resources [Core Portal]: https://github.com/TACC/Core-Portal + +[Docker]: https://docs.docker.com/get-docker/ +[Docker Compose]: https://docs.docker.com/compose/install/ +[Python]: https://www.python.org/downloads/ diff --git a/demdata_cms/docker-compose.dev.yml b/demdata_cms/docker-compose.dev.yml index 9268e03a..2aeb7843 100644 --- a/demdata_cms/docker-compose.dev.yml +++ b/demdata_cms/docker-compose.dev.yml @@ -3,7 +3,7 @@ services: cms: build: . ports: - - 8000:8000 + - 127.0.0.1:8000:8000 command: ["python3", "manage.py", "runserver", "0.0.0.0:8000"] container_name: core_cms hostname: core_cms @@ -44,7 +44,7 @@ services: - core_cms_es_data:/usr/share/elasticsearch/data container_name: core_cms_elasticsearch ports: - - 9201:9200 + - 127.0.0.1:9201:9200 networks: - core_cms_net diff --git a/demdata_cms/src/taccsite_cms/secrets.default.py b/demdata_cms/src/taccsite_cms/secrets.default.py deleted file mode 100644 index fbe11163..00000000 --- a/demdata_cms/src/taccsite_cms/secrets.default.py +++ /dev/null @@ -1,19 +0,0 @@ -# SECRET SETTINGS VALUES. -# (LOCAL TEST INSTANCE) - -######################## -# DATABASE SETTINGS -######################## - -from taccsite_cms.settings import DATABASES - -DATABASES = { - 'default': { - 'ENGINE': DATABASES['default']['ENGINE'], - 'PORT': DATABASES['default']['PORT'], - 'NAME': DATABASES['default']['NAME'], - 'USER': DATABASES['default']['USER'], - 'PASSWORD': DATABASES['default']['PASSWORD'], - 'HOST': DATABASES['default']['HOST'], - } -} diff --git a/demdata_cms/src/taccsite_cms/settings_custom.py b/demdata_cms/src/taccsite_cms/settings_custom.py index 1592e93c..5ead7035 100644 --- a/demdata_cms/src/taccsite_cms/settings_custom.py +++ b/demdata_cms/src/taccsite_cms/settings_custom.py @@ -24,10 +24,6 @@ ('demdata-cms/templates/standard.html', 'DEPRECATED Standard (with CSS from TUP)'), ('demdata-cms/templates/fullwidth.html', 'DEPRECATED Full Width (with CSS from TUP)'), ) -# CMS_TEMPLATES_DIR: { -# 1: 'demdata_cms/templates/', -# 2: 'demdata-cms/templates/', -# } ######################## # TACC: BRANDING diff --git a/docs/port-project.md b/docs/port-project.md new file mode 100644 index 00000000..c3091d1b --- /dev/null +++ b/docs/port-project.md @@ -0,0 +1,79 @@ +# Port Project + +## From [Core CMS Resources] + +To porting a downstream CMS project from [Core CMS Resources] to this repository: + +1. Copy contents: + - from [Core CMS Resources] `/taccsite_custom/custom_project_dir` + - to `/custom_project_dir/src/taccsite_custom/custom_project_dir` + + > **Warning** + > The name **must** use underscores (**not** dashes) to be a valid Python application. +2. Move custom project settings: + - from `/custom_project_dir/src/taccsite_custom/custom_project_dir/settings_custom.py` + - to `/custom_project_dir/src/taccsite_cms/settings_custom.py` +3. If the custom project has any edge cases, review relevant instructions: + - [Old Custom Templates Directory](#old-custom-templates-directory) + - [Expects CSS Build Step](expects-css-build-step) + - [Expects CSS Concatenation](expects-css-concatenation) + +### Old Custom Templates Directory + +**If** the custom project directory: + +- **both** had a name with dashes in [Core CMS Resources] +- **and** has `templates/standard.html` or `templates/fullwidth.html` + +Then: + +1. Copy the templates to become placeholders: + - from `taccsite_custom/custom_project_dir/templates` + - to `taccsite_custom/custom-project-dir/templates` + + > **Warning** + > The name `custom-project-dir` **must** match the old name as it was, including dashes. +2. Edit the placeholder templates to extend the new templates e.g. + + ```django + {% extends "custom_project_dir/templates/standard.html" %} + ``` + +### Expects CSS Build Step + +**If** the custom project directory expects CSS compilation e.g. has + +- `css/src/*.css` with `@import` of a `@tacc/core-styles/` path + +Then: + +1. Contact https://github.com/wesleyboar. + +> **Note** +> Those imports assume: +> +> - Node +> - NPM package `@tacc/core-styles` +> - a CSS build script +> +> Whether to support those here, and how to port without support for those, has not been decided. + +### Expects CSS Concatenation + +**If** the custom project directory expects CSS concatenation e.g. has + +- `css/src/*.css` with `@import` of a relative path + +Then: + +1. Rename import paths appended with comment `Core-CMS:/taccsite_cms/…/`: + - from `**/*.css` + - to `/static/site_cms/css/build/*.css` +2. Rename relative import paths (e.g. `./**/*.css`): + - from `**/*.css` + - to `/static/custom_project_dir/css/build/*.css` +3. Add UI test steps to initial deploy of ported custom project. + + + +[Core CMS Resources]: https://github.com/TACC/Core-CMS-Resources diff --git a/docs/run-project.md b/docs/run-project.md new file mode 100644 index 00000000..5b7a2032 --- /dev/null +++ b/docs/run-project.md @@ -0,0 +1,51 @@ +# Run Project + +## Individual Projects + +Read the relevant `custom_project_dir/README.md`. + +## Multiple Projects + +> **Note** +> By default, multiple projects can not be run simultaneously.[^1] + +To stop one project, and run another: + +1. Cancel any active `make start` output i.e. press control + C. + +2. Take down one project. + + > **Note** + > This is equivalent to deleting the relevant set of related containers in Docker Desktop. + + ```sh + cd custom_project_dir_1 + make stop + ``` + +3. Start another project. + + > **Note** + > This remove containers, but not volumes e.g. database. + + ```sh + cd custom_project_dir_2 + make start + ``` + +[^1]: If you want to run multiple projects simultaneously, see [Simultaneous Projects](#simultaneous-projects). + +## Simultaneous Projects + +> **Warning** +> With these instructions, you will **not** be able to use the database (**nor** internal search index) of an already set up custom project (i.e. its local volumes).[^2] + +To run multiple projects simultaneously: + +1. Stop and take down any started projects. (See [Multiple Projects](#multiple-projects) step 2.) +1. Create a custom `docker-compose.dev.yml` in your project. +2. Replace all instances of the text `core_cms` with the name of the `custom_project_dir`. + +[^2]: Advanced adaptation of these instructions may support retaining database access, et cetera. + +[copy a database]: https://confluence.tacc.utexas.edu/pages/viewpage.action?pageId=249135195 diff --git a/example_cms/docker-compose.dev.yml b/example_cms/docker-compose.dev.yml index ade79ea2..2aeb7843 100644 --- a/example_cms/docker-compose.dev.yml +++ b/example_cms/docker-compose.dev.yml @@ -5,8 +5,8 @@ services: ports: - 127.0.0.1:8000:8000 command: ["python3", "manage.py", "runserver", "0.0.0.0:8000"] - container_name: matcssi_cms - hostname: matcssi_cms + container_name: core_cms + hostname: core_cms volumes: - ./src/apps:/code/apps - ./src/taccsite_custom:/code/taccsite_custom @@ -16,7 +16,7 @@ services: - ./src/taccsite_cms/settings_local.py:/code/taccsite_cms/settings_local.py - ./src/taccsite_cms/secrets.py:/code/taccsite_cms/secrets.py networks: - - matcssi_cms_net + - core_cms_net postgres: image: postgres:11.5 @@ -26,11 +26,11 @@ services: - POSTGRES_DB=taccsite - PGDATA=/var/lib/postgresql/data/taccsite volumes: - - matcssi_cms_postgres_data:/var/lib/postgresql/data - hostname: matcssi_cms_postgres - container_name: matcssi_cms_postgres + - core_cms_postgres_data:/var/lib/postgresql/data + hostname: core_cms_postgres + container_name: core_cms_postgres networks: - - matcssi_cms_net + - core_cms_net elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.17.0 @@ -41,16 +41,16 @@ services: - discovery.type=single-node volumes: - ./elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml - - matcssi_cms_es_data:/usr/share/elasticsearch/data - container_name: matcssi_cms_elasticsearch + - core_cms_es_data:/usr/share/elasticsearch/data + container_name: core_cms_elasticsearch ports: - 127.0.0.1:9201:9200 networks: - - matcssi_cms_net + - core_cms_net volumes: - matcssi_cms_postgres_data: - matcssi_cms_es_data: + core_cms_postgres_data: + core_cms_es_data: networks: - matcssi_cms_net: + core_cms_net: diff --git a/example_cms/src/taccsite_cms/secrets.default.py b/example_cms/src/taccsite_cms/secrets.default.py deleted file mode 100644 index 0ea29773..00000000 --- a/example_cms/src/taccsite_cms/secrets.default.py +++ /dev/null @@ -1,19 +0,0 @@ -# SECRET SETTINGS VALUES. -# (LOCAL TEST INSTANCE) - -######################## -# DATABASE SETTINGS -######################## - -from taccsite_cms.settings import DATABASES - -DATABASES = { - 'default': { - 'ENGINE': DATABASES['default']['ENGINE'], - 'PORT': DATABASES['default']['PORT'], - 'NAME': DATABASES['default']['NAME'], - 'USER': DATABASES['default']['USER'], - 'PASSWORD': DATABASES['default']['PASSWORD'], - 'HOST': 'example_cms_postgres' - } -} diff --git a/example_cms/src/taccsite_custom/matcssi_cms/__init__.py b/example_cms/src/taccsite_custom/example_cms/__init__.py similarity index 100% rename from example_cms/src/taccsite_custom/matcssi_cms/__init__.py rename to example_cms/src/taccsite_custom/example_cms/__init__.py diff --git a/example_cms/src/taccsite_custom/matcssi_cms/static/matcssi_cms/img/favicons/favicon.ico b/example_cms/src/taccsite_custom/example_cms/static/example_cms/img/favicons/favicon.ico similarity index 100% rename from example_cms/src/taccsite_custom/matcssi_cms/static/matcssi_cms/img/favicons/favicon.ico rename to example_cms/src/taccsite_custom/example_cms/static/example_cms/img/favicons/favicon.ico diff --git a/example_cms/src/taccsite_custom/example_cms/static/example_cms/img/org_logos/portal.png b/example_cms/src/taccsite_custom/example_cms/static/example_cms/img/org_logos/portal.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec463826af8c20f80ba704c2f22977dd4334284 GIT binary patch literal 1913 zcmV-<2Zs2GP)36*LW^5qtmu02y>e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00!YnL_t(|+U=Zaj2*=h$A4un zHV(VQB%J1OVvEB@;D{?&99~!sbJ!4pEG$_>5JV(Epb!N6T?UDZ?l zz;PVMaU92S9LI4S$8j9TaU92S9LI5-@3Ce#C}|?_GvF{_Z(s(nGw>tehrkbjO~86! z2v`fO0#=yWznb_C#{i4dci9!_20jGd2Oa~Knb{i!+dcp|9GC^{nU3$rX&;k-4Z#0_ zH-Q0QIq-y;$yII(N751%XPu<;B~56c|AQs1$=Tl>lD2Kp_}-RuPMv){uX31QU|x5T z^lhZQ=Nif!}qI+uQ>jRpYT=xPdgr!@zdHy5ozO>lJ;$2e2=7u5M96yzzM*E8P8uL zY5NY*lM~a!hfJey;VwKbgY^3|>YN~HI8yJr8vX4qX(Ui@L{eYi`7TK-B7Oa(+2fOR zZbqe6xb^8vXn=(8s2LY>5ZyC17R`1Gh(tkF8Pt^oVP9ubHg~^fdyU4}^|f z7f71ic*tqSy}uH$H)2Kqw>5Y1j-+(5qP^@w~@8YG5Rw<$mT!*1H)tF-6hSR8%V0uF?u5MeIjF6V`jrO`db~5&njVTu|tS$IzUGj1Z0nz z*#_W+K-tM|AmxyC0x}el$4;Hz%F$o!nb{?bZO=N#Rh9T-4#^4NXg)Brwdvo>1JzD) z11X37BJwZI?2Aa*%!vGx9Q}P7kxy?V)g{8>i2RL;>qqG0-{YhH4jIT#8$2#|1F2Jq z{*zH>+XhzVp^Q0cn(HI!+zhwlnGTFU{yFOJ0g@A~aqDJP!ayXQ0h|#je!M_z@@1_) z)8pQeNO5-qW0@prnxqAi`iY;W3Am@i?$tDstmUz+95sjEkYr+yTLbB8VP6X+-2>bL zJWG=PejsoruqY$AU6#>iOp!-&zSD4I4vHAaX_D$mXTQ(P1}m%^C$o8S;%8>|-!Zc1 zNHP>gGgs17GaGUP$ z$ts0S4p$oK#K7d@G(hxNk8xh;1DyLZDx1Y zad$hm66Z-Hk3I9hf_KLsu$0sYLLVZhlceTXoo226$GCwMP#`m#VrDy;*(@_V&CISe zvw;F_u8GKJO?H&O0a{M3MK+3|*Hbw%+Zo*l zayxKYfvSVRiK#18CU+7}joF)O+`^kfIJ;n&Brh%xFGb`>xPer|NUk7B=Xf{IqM5or z2ecw_(9G5YcSVXXuE6xoi^$icJ769LGUAYq)HP!qB54xIZ#gXj_6K$$`8M)V8jm~z z+-+uW;W&=tIF92uj^j9v<2a7vIF92uTOi*7L5of`p4>6(00000NkvXXu0mjfooa-M literal 0 HcmV?d00001 diff --git a/example_cms/src/taccsite_custom/matcssi_cms/static/matcssi_cms/img/org_logos/matcssi.png b/example_cms/src/taccsite_custom/matcssi_cms/static/matcssi_cms/img/org_logos/matcssi.png deleted file mode 100644 index 4f98b17769c9fcdb9e660b757c87f3753c0af2c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14074 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