We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
💡 Combine Github release workflows
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Is your proposal related to a problem or functionality gap?

Currently the release workflow for dev/test/production is 3 separate Github actions, working with Gitflow. So merging a new fix requires 3 separate PRs, artifacts, and deployments: https://github.com/Health-Informatics-UoN/CaRROT-Mapper/pulls?q=is%3Apr+is%3Aclosed



### Describe your proposal

I propose combining this into one workflow, utilising Github environments to deploy a single artifact from the main branch. An example here: https://github.com/Health-Informatics-UoN/rcc-monitor/actions/runs/7931015704


### Describe alternatives you've considered

_No response_

### I'm part of a Project Team

Yes - DRS

### Anything else?

It might not be done here - but I think it's worth also exploring when database migrations happen as part of CD, not sure if this currently a manual process?

### Are you willing to contribute to developing this feature?

✅ Yes, me or my team intend to do the development.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `app/api/api/settings.py`
Content:
```
1 """
2 Django settings for api project.
3 
4 Generated by 'django-admin startproject' using Django 3.1.5.
5 
6 For more information on this file, see
7 https://docs.djangoproject.com/en/3.1/topics/settings/
8 
9 For the full list of settings and their values, see
10 https://docs.djangoproject.com/en/3.1/ref/settings/
11 """
12 
13 import os
14 from dotenv import load_dotenv
15 
16 load_dotenv()
17 
18 # Build paths inside the project like this: BASE_DIR / 'subdir'.
19 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
20 TEMPLATE_DIR = os.path.join(BASE_DIR, "templates")
21 STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
22 STATIC_DIR = os.path.join(BASE_DIR, "static")
23 STATICFILES_DIRS = [
24     STATIC_DIR,
25 ]
26 MEDIA_URL = "/media/"
27 MEDIA_ROOT = os.path.join(BASE_DIR, "media")
28 
29 # Quick-start development settings - unsuitable for production
30 # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
31 
32 # SECURITY WARNING: keep the secret key used in production secret!
33 SECRET_KEY = os.environ.get("SECRET_KEY")
34 
35 # SECURITY WARNING: don't run with debug turned on in production!
36 # DEBUG will only evaluate to True if 'True' or 1 is supplied
37 DEBUG = os.getenv("DEBUG", "False") in ["True", 1]
38 
39 # Here we need to manipulate a string containing a Python list into a list of strings
40 ALLOWED_HOSTS = [
41     x.strip()[1:-1] for x in os.environ.get("ALLOWED_HOSTS")[1:-1].split(",")
42 ]
43 
44 
45 # Application definition
46 
47 INSTALLED_APPS = [
48     "django.contrib.admin",
49     "django.contrib.auth",
50     "django.contrib.contenttypes",
51     "django.contrib.sessions",
52     "django.contrib.messages",
53     "whitenoise.runserver_nostatic",
54     "django.contrib.staticfiles",
55     "extra_views",
56     "mapping",
57     "data",
58     "rest_framework",
59     "django_filters",
60     "rest_framework.authtoken",
61     "corsheaders",
62 ]
63 
64 MIDDLEWARE = [
65     "corsheaders.middleware.CorsMiddleware",
66     "django.middleware.security.SecurityMiddleware",
67     "django.contrib.sessions.middleware.SessionMiddleware",
68     "django.middleware.common.CommonMiddleware",
69     "django.middleware.csrf.CsrfViewMiddleware",
70     "django.contrib.auth.middleware.AuthenticationMiddleware",
71     "django.contrib.messages.middleware.MessageMiddleware",
72     "django.middleware.clickjacking.XFrameOptionsMiddleware",
73     "whitenoise.middleware.WhiteNoiseMiddleware",
74 ]
75 
76 STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
77 
78 ROOT_URLCONF = "api.urls"
79 
80 TEMPLATES = [
81     {
82         "BACKEND": "django.template.backends.django.DjangoTemplates",
83         "DIRS": [os.path.join(BASE_DIR, "templates")],
84         "APP_DIRS": True,
85         "OPTIONS": {
86             "context_processors": [
87                 "django.template.context_processors.debug",
88                 "django.template.context_processors.request",
89                 "django.contrib.auth.context_processors.auth",
90                 "django.contrib.messages.context_processors.messages",
91                 "django.template.context_processors.media",
92                 "api.react.react",
93             ],
94         },
95     },
96 ]
97 
98 WSGI_APPLICATION = "api.wsgi.application"
99 
100 
101 # Database
102 # https://docs.djangoproject.com/en/3.1/ref/settings/#databases
103 
104 DATABASES = {
105     "default": {
106         "ENGINE": os.getenv("COCONNECT_DB_ENGINE"),
107         "HOST": os.getenv("COCONNECT_DB_HOST"),
108         "PORT": os.getenv("COCONNECT_DB_PORT"),
109         "NAME": os.getenv("COCONNECT_DB_NAME"),
110         "USER": os.getenv("COCONNECT_DB_USER"),
111         "PASSWORD": os.getenv("COCONNECT_DB_PASSWORD"),
112         "TEST": {
113             "NAME": "throwaway-db",
114             "MIRROR": "default",
115         },
116     }
117 }
118 
119 
120 # Password validation
121 # https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
122 
123 AUTH_PASSWORD_VALIDATORS = [
124     {
125         "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
126     },
127     {
128         "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
129     },
130     {
131         "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
132     },
133     {
134         "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
135     },
136 ]
137 
138 
139 # Internationalization
140 # https://docs.djangoproject.com/en/3.1/topics/i18n/
141 
142 LANGUAGE_CODE = "en-us"
143 
144 TIME_ZONE = "UTC"
145 
146 USE_I18N = True
147 
148 USE_L10N = True
149 
150 USE_TZ = True
151 
152 CORS_ORIGIN_ALLOW_ALL = True
153 
154 REST_FRAMEWORK = {
155     "DEFAULT_AUTHENTICATION_CLASSES": (
156         "rest_framework.authentication.TokenAuthentication",
157         "rest_framework.authentication.SessionAuthentication",
158     ),
159     "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
160 }
161 # Static files (CSS, JavaScript, Images)
162 # https://docs.djangoproject.com/en/3.1/howto/static-files/
163 
164 STATIC_URL = "/static/"
165 
166 LOGIN_REDIRECT_URL = "/"
167 EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
168 
169 # NLP API KEY
170 NLP_API_KEY = os.getenv("NLP_API_KEY")
171 
172 SESSION_COOKIE_AGE = 86400  # session length is 24 hours
173
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/app/api/api/settings.py b/app/api/api/settings.py
--- a/app/api/api/settings.py
+++ b/app/api/api/settings.py
@@ -11,6 +11,7 @@
 """
 
 import os
+
 from dotenv import load_dotenv
 
 load_dotenv()
@@ -59,6 +60,7 @@
     "django_filters",
     "rest_framework.authtoken",
     "corsheaders",
+    "test",
 ]
 
 MIDDLEWARE = [
@@ -110,8 +112,7 @@
         "USER": os.getenv("COCONNECT_DB_USER"),
         "PASSWORD": os.getenv("COCONNECT_DB_PASSWORD"),
         "TEST": {
-            "NAME": "throwaway-db",
-            "MIRROR": "default",
+            "NAME": "throwawaydb",
         },
     }
 }

{"golden_diff": "diff --git a/app/api/api/settings.py b/app/api/api/settings.py\n--- a/app/api/api/settings.py\n+++ b/app/api/api/settings.py\n@@ -11,6 +11,7 @@\n \"\"\"\n \n import os\n+\n from dotenv import load_dotenv\n \n load_dotenv()\n@@ -59,6 +60,7 @@\n     \"django_filters\",\n     \"rest_framework.authtoken\",\n     \"corsheaders\",\n+    \"test\",\n ]\n \n MIDDLEWARE = [\n@@ -110,8 +112,7 @@\n         \"USER\": os.getenv(\"COCONNECT_DB_USER\"),\n         \"PASSWORD\": os.getenv(\"COCONNECT_DB_PASSWORD\"),\n         \"TEST\": {\n-            \"NAME\": \"throwaway-db\",\n-            \"MIRROR\": \"default\",\n+            \"NAME\": \"throwawaydb\",\n         },\n     }\n }\n", "issue": "\ud83d\udca1 Combine Github release workflows\n### Is there an existing issue for this?\r\n\r\n- [X] I have searched the existing issues\r\n\r\n### Is your proposal related to a problem or functionality gap?\r\n\r\nCurrently the release workflow for dev/test/production is 3 separate Github actions, working with Gitflow. So merging a new fix requires 3 separate PRs, artifacts, and deployments: https://github.com/Health-Informatics-UoN/CaRROT-Mapper/pulls?q=is%3Apr+is%3Aclosed\r\n\r\n\r\n\r\n### Describe your proposal\r\n\r\nI propose combining this into one workflow, utilising Github environments to deploy a single artifact from the main branch. An example here: https://github.com/Health-Informatics-UoN/rcc-monitor/actions/runs/7931015704\r\n\r\n\r\n### Describe alternatives you've considered\r\n\r\n_No response_\r\n\r\n### I'm part of a Project Team\r\n\r\nYes - DRS\r\n\r\n### Anything else?\r\n\r\nIt might not be done here - but I think it's worth also exploring when database migrations happen as part of CD, not sure if this currently a manual process?\r\n\r\n### Are you willing to contribute to developing this feature?\r\n\r\n\u2705 Yes, me or my team intend to do the development.\n", "before_files": [{"content": "\"\"\"\nDjango settings for api project.\n\nGenerated by 'django-admin startproject' using Django 3.1.5.\n\nFor more information on this file, see\nhttps://docs.djangoproject.com/en/3.1/topics/settings/\n\nFor the full list of settings and their values, see\nhttps://docs.djangoproject.com/en/3.1/ref/settings/\n\"\"\"\n\nimport os\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\n# Build paths inside the project like this: BASE_DIR / 'subdir'.\nBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\nTEMPLATE_DIR = os.path.join(BASE_DIR, \"templates\")\nSTATIC_ROOT = os.path.join(BASE_DIR, \"staticfiles\")\nSTATIC_DIR = os.path.join(BASE_DIR, \"static\")\nSTATICFILES_DIRS = [\n    STATIC_DIR,\n]\nMEDIA_URL = \"/media/\"\nMEDIA_ROOT = os.path.join(BASE_DIR, \"media\")\n\n# Quick-start development settings - unsuitable for production\n# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/\n\n# SECURITY WARNING: keep the secret key used in production secret!\nSECRET_KEY = os.environ.get(\"SECRET_KEY\")\n\n# SECURITY WARNING: don't run with debug turned on in production!\n# DEBUG will only evaluate to True if 'True' or 1 is supplied\nDEBUG = os.getenv(\"DEBUG\", \"False\") in [\"True\", 1]\n\n# Here we need to manipulate a string containing a Python list into a list of strings\nALLOWED_HOSTS = [\n    x.strip()[1:-1] for x in os.environ.get(\"ALLOWED_HOSTS\")[1:-1].split(\",\")\n]\n\n\n# Application definition\n\nINSTALLED_APPS = [\n    \"django.contrib.admin\",\n    \"django.contrib.auth\",\n    \"django.contrib.contenttypes\",\n    \"django.contrib.sessions\",\n    \"django.contrib.messages\",\n    \"whitenoise.runserver_nostatic\",\n    \"django.contrib.staticfiles\",\n    \"extra_views\",\n    \"mapping\",\n    \"data\",\n    \"rest_framework\",\n    \"django_filters\",\n    \"rest_framework.authtoken\",\n    \"corsheaders\",\n]\n\nMIDDLEWARE = [\n    \"corsheaders.middleware.CorsMiddleware\",\n    \"django.middleware.security.SecurityMiddleware\",\n    \"django.contrib.sessions.middleware.SessionMiddleware\",\n    \"django.middleware.common.CommonMiddleware\",\n    \"django.middleware.csrf.CsrfViewMiddleware\",\n    \"django.contrib.auth.middleware.AuthenticationMiddleware\",\n    \"django.contrib.messages.middleware.MessageMiddleware\",\n    \"django.middleware.clickjacking.XFrameOptionsMiddleware\",\n    \"whitenoise.middleware.WhiteNoiseMiddleware\",\n]\n\nSTATICFILES_STORAGE = \"whitenoise.storage.CompressedManifestStaticFilesStorage\"\n\nROOT_URLCONF = \"api.urls\"\n\nTEMPLATES = [\n    {\n        \"BACKEND\": \"django.template.backends.django.DjangoTemplates\",\n        \"DIRS\": [os.path.join(BASE_DIR, \"templates\")],\n        \"APP_DIRS\": True,\n        \"OPTIONS\": {\n            \"context_processors\": [\n                \"django.template.context_processors.debug\",\n                \"django.template.context_processors.request\",\n                \"django.contrib.auth.context_processors.auth\",\n                \"django.contrib.messages.context_processors.messages\",\n                \"django.template.context_processors.media\",\n                \"api.react.react\",\n            ],\n        },\n    },\n]\n\nWSGI_APPLICATION = \"api.wsgi.application\"\n\n\n# Database\n# https://docs.djangoproject.com/en/3.1/ref/settings/#databases\n\nDATABASES = {\n    \"default\": {\n        \"ENGINE\": os.getenv(\"COCONNECT_DB_ENGINE\"),\n        \"HOST\": os.getenv(\"COCONNECT_DB_HOST\"),\n        \"PORT\": os.getenv(\"COCONNECT_DB_PORT\"),\n        \"NAME\": os.getenv(\"COCONNECT_DB_NAME\"),\n        \"USER\": os.getenv(\"COCONNECT_DB_USER\"),\n        \"PASSWORD\": os.getenv(\"COCONNECT_DB_PASSWORD\"),\n        \"TEST\": {\n            \"NAME\": \"throwaway-db\",\n            \"MIRROR\": \"default\",\n        },\n    }\n}\n\n\n# Password validation\n# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators\n\nAUTH_PASSWORD_VALIDATORS = [\n    {\n        \"NAME\": \"django.contrib.auth.password_validation.UserAttributeSimilarityValidator\",\n    },\n    {\n        \"NAME\": \"django.contrib.auth.password_validation.MinimumLengthValidator\",\n    },\n    {\n        \"NAME\": \"django.contrib.auth.password_validation.CommonPasswordValidator\",\n    },\n    {\n        \"NAME\": \"django.contrib.auth.password_validation.NumericPasswordValidator\",\n    },\n]\n\n\n# Internationalization\n# https://docs.djangoproject.com/en/3.1/topics/i18n/\n\nLANGUAGE_CODE = \"en-us\"\n\nTIME_ZONE = \"UTC\"\n\nUSE_I18N = True\n\nUSE_L10N = True\n\nUSE_TZ = True\n\nCORS_ORIGIN_ALLOW_ALL = True\n\nREST_FRAMEWORK = {\n    \"DEFAULT_AUTHENTICATION_CLASSES\": (\n        \"rest_framework.authentication.TokenAuthentication\",\n        \"rest_framework.authentication.SessionAuthentication\",\n    ),\n    \"DEFAULT_PERMISSION_CLASSES\": (\"rest_framework.permissions.IsAuthenticated\",),\n}\n# Static files (CSS, JavaScript, Images)\n# https://docs.djangoproject.com/en/3.1/howto/static-files/\n\nSTATIC_URL = \"/static/\"\n\nLOGIN_REDIRECT_URL = \"/\"\nEMAIL_BACKEND = \"django.core.mail.backends.console.EmailBackend\"\n\n# NLP API KEY\nNLP_API_KEY = os.getenv(\"NLP_API_KEY\")\n\nSESSION_COOKIE_AGE = 86400  # session length is 24 hours\n", "path": "app/api/api/settings.py"}], "after_files": [{"content": "\"\"\"\nDjango settings for api project.\n\nGenerated by 'django-admin startproject' using Django 3.1.5.\n\nFor more information on this file, see\nhttps://docs.djangoproject.com/en/3.1/topics/settings/\n\nFor the full list of settings and their values, see\nhttps://docs.djangoproject.com/en/3.1/ref/settings/\n\"\"\"\n\nimport os\n\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\n# Build paths inside the project like this: BASE_DIR / 'subdir'.\nBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\nTEMPLATE_DIR = os.path.join(BASE_DIR, \"templates\")\nSTATIC_ROOT = os.path.join(BASE_DIR, \"staticfiles\")\nSTATIC_DIR = os.path.join(BASE_DIR, \"static\")\nSTATICFILES_DIRS = [\n    STATIC_DIR,\n]\nMEDIA_URL = \"/media/\"\nMEDIA_ROOT = os.path.join(BASE_DIR, \"media\")\n\n# Quick-start development settings - unsuitable for production\n# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/\n\n# SECURITY WARNING: keep the secret key used in production secret!\nSECRET_KEY = os.environ.get(\"SECRET_KEY\")\n\n# SECURITY WARNING: don't run with debug turned on in production!\n# DEBUG will only evaluate to True if 'True' or 1 is supplied\nDEBUG = os.getenv(\"DEBUG\", \"False\") in [\"True\", 1]\n\n# Here we need to manipulate a string containing a Python list into a list of strings\nALLOWED_HOSTS = [\n    x.strip()[1:-1] for x in os.environ.get(\"ALLOWED_HOSTS\")[1:-1].split(\",\")\n]\n\n\n# Application definition\n\nINSTALLED_APPS = [\n    \"django.contrib.admin\",\n    \"django.contrib.auth\",\n    \"django.contrib.contenttypes\",\n    \"django.contrib.sessions\",\n    \"django.contrib.messages\",\n    \"whitenoise.runserver_nostatic\",\n    \"django.contrib.staticfiles\",\n    \"extra_views\",\n    \"mapping\",\n    \"data\",\n    \"rest_framework\",\n    \"django_filters\",\n    \"rest_framework.authtoken\",\n    \"corsheaders\",\n    \"test\",\n]\n\nMIDDLEWARE = [\n    \"corsheaders.middleware.CorsMiddleware\",\n    \"django.middleware.security.SecurityMiddleware\",\n    \"django.contrib.sessions.middleware.SessionMiddleware\",\n    \"django.middleware.common.CommonMiddleware\",\n    \"django.middleware.csrf.CsrfViewMiddleware\",\n    \"django.contrib.auth.middleware.AuthenticationMiddleware\",\n    \"django.contrib.messages.middleware.MessageMiddleware\",\n    \"django.middleware.clickjacking.XFrameOptionsMiddleware\",\n    \"whitenoise.middleware.WhiteNoiseMiddleware\",\n]\n\nSTATICFILES_STORAGE = \"whitenoise.storage.CompressedManifestStaticFilesStorage\"\n\nROOT_URLCONF = \"api.urls\"\n\nTEMPLATES = [\n    {\n        \"BACKEND\": \"django.template.backends.django.DjangoTemplates\",\n        \"DIRS\": [os.path.join(BASE_DIR, \"templates\")],\n        \"APP_DIRS\": True,\n        \"OPTIONS\": {\n            \"context_processors\": [\n                \"django.template.context_processors.debug\",\n                \"django.template.context_processors.request\",\n                \"django.contrib.auth.context_processors.auth\",\n                \"django.contrib.messages.context_processors.messages\",\n                \"django.template.context_processors.media\",\n                \"api.react.react\",\n            ],\n        },\n    },\n]\n\nWSGI_APPLICATION = \"api.wsgi.application\"\n\n\n# Database\n# https://docs.djangoproject.com/en/3.1/ref/settings/#databases\n\nDATABASES = {\n    \"default\": {\n        \"ENGINE\": os.getenv(\"COCONNECT_DB_ENGINE\"),\n        \"HOST\": os.getenv(\"COCONNECT_DB_HOST\"),\n        \"PORT\": os.getenv(\"COCONNECT_DB_PORT\"),\n        \"NAME\": os.getenv(\"COCONNECT_DB_NAME\"),\n        \"USER\": os.getenv(\"COCONNECT_DB_USER\"),\n        \"PASSWORD\": os.getenv(\"COCONNECT_DB_PASSWORD\"),\n        \"TEST\": {\n            \"NAME\": \"throwawaydb\",\n        },\n    }\n}\n\n\n# Password validation\n# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators\n\nAUTH_PASSWORD_VALIDATORS = [\n    {\n        \"NAME\": \"django.contrib.auth.password_validation.UserAttributeSimilarityValidator\",\n    },\n    {\n        \"NAME\": \"django.contrib.auth.password_validation.MinimumLengthValidator\",\n    },\n    {\n        \"NAME\": \"django.contrib.auth.password_validation.CommonPasswordValidator\",\n    },\n    {\n        \"NAME\": \"django.contrib.auth.password_validation.NumericPasswordValidator\",\n    },\n]\n\n\n# Internationalization\n# https://docs.djangoproject.com/en/3.1/topics/i18n/\n\nLANGUAGE_CODE = \"en-us\"\n\nTIME_ZONE = \"UTC\"\n\nUSE_I18N = True\n\nUSE_L10N = True\n\nUSE_TZ = True\n\nCORS_ORIGIN_ALLOW_ALL = True\n\nREST_FRAMEWORK = {\n    \"DEFAULT_AUTHENTICATION_CLASSES\": (\n        \"rest_framework.authentication.TokenAuthentication\",\n        \"rest_framework.authentication.SessionAuthentication\",\n    ),\n    \"DEFAULT_PERMISSION_CLASSES\": (\"rest_framework.permissions.IsAuthenticated\",),\n}\n# Static files (CSS, JavaScript, Images)\n# https://docs.djangoproject.com/en/3.1/howto/static-files/\n\nSTATIC_URL = \"/static/\"\n\nLOGIN_REDIRECT_URL = \"/\"\nEMAIL_BACKEND = \"django.core.mail.backends.console.EmailBackend\"\n\n# NLP API KEY\nNLP_API_KEY = os.getenv(\"NLP_API_KEY\")\n\nSESSION_COOKIE_AGE = 86400  # session length is 24 hours\n", "path": "app/api/api/settings.py"}]}