Skip to content

Commit 1f7013e

Browse files
committed
Merge branch 'release/3.3.0'
2 parents f0fb152 + 63e9c38 commit 1f7013e

File tree

9 files changed

+302
-225
lines changed

9 files changed

+302
-225
lines changed

CHANGELOG.md

Lines changed: 127 additions & 207 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ This project provides a virtual machine wherein it hosts the local REDCap instan
2323

2424
### Packaging and Deployment
2525

26-
The packaging and deployment tools are designed to deploy REDCap to Debian Linux hosts. They may or may not work with non-Debian REDCap hosts. They cannot deploy REDCap to Windows hosts. The packaging and deployment tools are written using the [Fabric](http://www.fabfile.org/) system. Fabric is written in Python3, so both Python 3 and Fabric3 must be installed to do packaging and deployment.
26+
The packaging and deployment tools are designed to deploy REDCap to Debian Linux hosts. They may or may not work with non-Debian REDCap hosts. They cannot deploy REDCap to Windows hosts. The packaging and deployment tools are written using the [Fabric](http://www.fabfile.org/) system. The tools use Fabric 1.x
2727

2828

2929
## Installing dependencies
@@ -117,7 +117,7 @@ After about two minutes, the VM should be accessible at the value of the variabl
117117

118118
## (Re)deploying REDCap with Fabric Tools
119119

120-
In addition to the REDCap deployed by the Vagrant provisioning scripts, this repository includes a suite of deployment and upgrade tools that can configure a host for deployment, package REDCap with numerous extensions, deploy a new REDCap instance and upgrade an existing one. You can use these commands any host where you have sufficient privileges or against this vagrant-deployed VM.
120+
In addition to the REDCap deployed by the Vagrant provisioning scripts, this repository includes a suite of deployment and upgrade tools that can configure a host for deployment, package REDCap with numerous extensions, deploy a new REDCap instance and upgrade an existing one. You can use these commands on any Linux host where you have sufficient privileges or against this vagrant-deployed VM.
121121

122122
### Fabric Prerequisites
123123

@@ -127,6 +127,14 @@ This tool is written in Python 3 and uses Fabric 1.x methods. To use it, make su
127127
pip install 'fabric<2.0'
128128
```
129129

130+
If you make a mess of things before you get it right, you might have to uninstall the mess like this:
131+
132+
```bash
133+
brew uninstall fabric
134+
pip uninstall 'fabric<2.0'
135+
pip install 'fabric<2.0'
136+
```
137+
130138

131139
### Configure Fabric for the Virtual Machine
132140

@@ -277,6 +285,54 @@ sudo a2enmod php8.1
277285
sudo systemctl restart apache2
278286
```
279287

288+
### Upgrade PHP from 7.4 to 8.2
289+
290+
```bash
291+
# Upgrade PHP from 7.4 to 8.2
292+
sudo apt install -y libapache2-mod-php8.2 \
293+
php8.2 \
294+
php8.2-cli \
295+
php8.2-common \
296+
php8.2-curl \
297+
php8.2-gd \
298+
php8.2-imap \
299+
php8.2-mbstring \
300+
php8.2-mysql \
301+
php8.2-odbc \
302+
php8.2-opcache \
303+
php8.2-readline \
304+
php8.2-soap \
305+
php8.2-xml \
306+
php8.2-zip
307+
308+
cd /etc
309+
sudo -E git add .
310+
sudo -E git commit -m "Commit PHP upgrades and other files"
311+
312+
cd /etc/php
313+
grep -lr upload_max_filesize * | sudo xargs -i sed "s/upload_max_filesize.*/upload_max_filesize = 256M/;" -i {}
314+
grep -lr post_max_size * | sudo xargs -i sed "s/post_max_size.*/post_max_size = 256M/;" -i {}
315+
grep -lr max_input_vars * | sudo xargs -i sed "s/.*max_input_vars.*/max_input_vars = 100000/;" -i {}
316+
grep -lr session.cookie_secure * | sudo xargs -i sed "s/.*session.cookie_secure.*/session.cookie_secure = On/;" -i {}
317+
grep -lr date.timezone * | sudo xargs -i sed "s/.*date.timezone.*=.*/date.timezone = 'America\/New_York'/;" -i {}
318+
319+
cd /etc
320+
sudo -E git add .
321+
sudo -E git commit -m "Commit PHP configuration changes"
322+
323+
# fix imagick
324+
sudo apt install -y php-imagick
325+
sudo sed -i 's/policy domain="coder" rights="none" pattern="PDF"/policy domain="coder" rights="read" pattern="PDF"/;' /etc/ImageMagick-6/policy.xml
326+
cd /etc
327+
sudo -E git add .
328+
sudo -E git commit -m "Install php-imagick and adjust policy to REDCap requirements"
329+
330+
# Switch to new PHP in Apache
331+
sudo a2dismod php7.4
332+
sudo a2enmod php8.2
333+
sudo systemctl restart apache2
334+
```
335+
280336
### Install specific PHP packages
281337

282338
On some hosts, you might need to install a specific packages. At UF, we have one host we call "warrior" that needs the `mpdf` package. To install a custom package, first, install `composer`
@@ -296,8 +352,8 @@ Then run these steps in the current redcap Libraries directory as user deploy
296352

297353
```bash
298354
sudo su - deploy
299-
#cd /var/https/stage_c/redcap_v13.4.11/Libraries/
300-
cd /var/www/prod/redcap_v13.4.11/Libraries/
355+
#cd /var/https/stage_c/redcap_v14.3.14/Libraries/
356+
cd /var/www/prod/redcap_v14.6.2/Libraries/
301357
```
302358

303359
Then run `composer require` with the package you need install:
@@ -313,6 +369,45 @@ Restart apache when you are done
313369
sudo service apache2 restart
314370
```
315371

372+
## Failed SQL upgrades
373+
374+
If an upgrade operation fails on a `.php` or a `.sql` file, the code will die like this:
375+
376+
```
377+
[redcap.ctsi.ufl.edu] run: php /var/https/redcap/redcap_v14.1.3/generate_upgrade_sql_from_php.php /var/https/redcap/redcap_v14.1.3/Resources/sql/upgrade_14.01.01.php | mysql
378+
[redcap.ctsi.ufl.edu] out: ERROR 1553 (HY000) at line 5: Cannot drop index 'redcap_userid': needed in a foreign key constraint
379+
[redcap.ctsi.ufl.edu] out:
380+
381+
382+
Fatal error: run() received nonzero return code 1 while executing!
383+
384+
Requested: php /var/https/redcap/redcap_v14.1.3/generate_upgrade_sql_from_php.php /var/https/redcap/redcap_v14.1.3/Resources/sql/upgrade_14.01.01.php | mysql
385+
Executed: /bin/bash -l -c "php /var/https/redcap/redcap_v14.1.3/generate_upgrade_sql_from_php.php /var/https/redcap/redcap_v14.1.3/Resources/sql/upgrade_14.01.01.php | mysql"
386+
387+
Aborting.
388+
Disconnecting from [email protected]... done.
389+
```
390+
391+
At this point things get messy and you will have to get creative in your problem solving. What follows is not a recipe. It's more like a pile of ingredients from which you could make an omelette. It's still your responsibility cook them right and not burn the omelette, because you're gonna have to eat it.
392+
393+
### Debugging
394+
395+
You can see the output of the script/SQl file that caused the by running that PHP command or catting out that SQL file that failed. e.g.,
396+
397+
```bash
398+
php /var/https/redcap/redcap_v14.1.3/generate_upgrade_sql_from_php.php /var/https/redcap/redcap_v14.1.3/Resources/sql/upgrade_14.01.01.php
399+
```
400+
401+
Running the SQL lines in that file one by one might help you understand which one failed. It's challenging because some of those commands will fail because they _did_ run successfully the first time, but they will error the second time. This often happens when a SQL command added an object that cannot be "re-added". Think carefully about the responses you see when running each command. Read the REDCap community forum to see if others have experienced the errors you see and have a solution.
402+
403+
Even if that task goes well and you can apply the file that failed there might be more files to run after that one. You can locate those by enumerating the files in the `redcap_vN.M.O/Resources/sql/` directory. Look for versioned file names that are higher than the version number on which the script failed. If this seems tedious, you might want to try the next procedure.
404+
405+
406+
### Ask REDCap to help you
407+
408+
Another approach is to generate all of the SQL upgrade commands between the version you are at and the version you are upgrading to by accessing the Upgrade page in the Control Center. It will make the whole blob, running the PHP scripts, concatenating their output with the SQL files and appending the lines that document the upgrade. All that's pretty useful. Do be aware that every SQL line that succeeded will still be in the blob of SQL the Control Center Upgrade page generates for you. It's more content to wade through, but it's all in one file with the last three "upgrade event" statements.
409+
410+
If you think you are done with the upgrade process, put the host back online via the Control Center _General Configuration_ page. Then check the Control Center _Configuration Check_ age for any SQL commands that still need to be applied.
316411

317412
## Developer notes
318413

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.2.1
1+
3.3.0

composer.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
{
2+
"config": {
3+
"allow-plugins": false
4+
},
25
"require": {
36
"wikimedia/composer-merge-plugin": "dev-master"
47
},

deploy/composer/deploy.sh

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ cd $MYTARGETDIR
1010
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
1111

1212
# install composer
13-
# pin to version 1.latest due to 2.x conflict: https://github.com/wikimedia/composer-merge-plugin/issues/184
14-
php composer-setup.php --1
13+
php composer-setup.php
1514

1615
# install dependencies
1716
php composer.phar install

package.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@ def deploy_modules_into_build_space():
9797
local("cp -r %s/* %s/redcap/modules/%s_v%s" % (tempdir,env.builddir,module['name'],module['version']))
9898
local("rm -rf %s" % tempdir)
9999

100+
def deploy_redcap_redirect_into_build_space():
101+
"""
102+
Deploy redcap_redirect into build space
103+
"""
104+
tempdir = local("mktemp -d 2>&1", capture = True)
105+
local("git clone %s %s" % ("https://github.com/susom/redcap-redirect.git",tempdir))
106+
local("cp -r %s/redcap_redirect.php %s/redcap/" % (tempdir,env.builddir))
107+
local("rm -rf %s" % tempdir)
100108

101109
def deploy_plugins_into_build_space(target_within_build_space="/redcap/plugins"):
102110
"""
@@ -180,6 +188,7 @@ def package(redcap_zip="."):
180188
clean(env.builddir)
181189
make_builddir(env.builddir)
182190
redcap_version_and_package_type = extract_redcap(redcap_zip)
191+
deploy_redcap_redirect_into_build_space()
183192
deploy_plugins_into_build_space()
184193
deploy_modules_into_build_space()
185194
# Deploy modules framework only if redcap major version is less than 8

upgrade.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,17 @@ def apply_incremental_db_changes(old, new):
8888
path_to_sql_generation = '/'.join([env.live_pre_path, env.project_path, 'redcap_v' + new, 'generate_upgrade_sql_from_php.php'])
8989
for file in files.splitlines():
9090
match = re.search(r"(upgrade_)(\d+.\d+.\d+)(.)(php|sql)", file)
91-
version = match.group(2)
92-
version = utility.convert_version_to_int(version)
93-
if version > old and version <= new_as_an_int:
94-
if fnmatch.fnmatch(file, "*.php"):
95-
print((file + " is a php file!\n"))
96-
with settings(user=env.deploy_user):
97-
run('php %s %s | mysql' % (path_to_sql_generation,file))
98-
else:
99-
print(("Executing sql file %s" % file))
100-
with settings(user=env.deploy_user):
101-
run('mysql < %s' % file)
91+
if match is not None:
92+
version = utility.convert_version_to_int(match.group(2))
93+
if version > old and version <= new_as_an_int:
94+
if fnmatch.fnmatch(file, "*.php"):
95+
print((file + " is a php file!\n"))
96+
with settings(user=env.deploy_user):
97+
run('php %s %s | mysql' % (path_to_sql_generation,file))
98+
else:
99+
print(("Executing sql file %s" % file))
100+
with settings(user=env.deploy_user):
101+
run('mysql < %s' % file)
102102
# Finalize upgrade
103103
utility_redcap.set_redcap_config('redcap_last_install_date', datetime.now().strftime("%Y-%m-%d"))
104104
utility_redcap.set_redcap_config('redcap_version', new)

utility_redcap.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from tempfile import mkstemp
33
import os
44
import utility
5+
import utility_test
56
try:
67
import configparser
78
except:
@@ -108,6 +109,7 @@ def test(warn_only=False):
108109
utility.write_remote_my_cnf()
109110
version = get_current_redcap_version()
110111
utility.delete_remote_my_cnf()
112+
utility_test.main()
111113
local("python tests/test.py %s/ redcap_v%s/" % (env.url_of_deployed_app,version))
112114
with settings(warn_only=True):
113115
if local("python tests/test.py %s/ redcap_v%s/" % (env.url_of_deployed_app,version)).failed:
@@ -118,3 +120,4 @@ def test(warn_only=False):
118120
abort("One or more tests failed.")
119121
else:
120122
return(True)
123+

utility_test.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from fabric.api import *
2+
import unittest
3+
4+
class CheckHostAccessibility(unittest.TestCase):
5+
def setUp(self):
6+
self.hosts = [
7+
"https://api.reporter.nih.gov",
8+
"https://eutils.ncbi.nlm.nih.gov",
9+
"https://www.ncbi.nlm.nih.gov",
10+
"https://icite.od.nih.gov",
11+
"https://pub.orcid.org",
12+
"https://redcap.vanderbilt.edu",
13+
"https://api.altmetric.com",
14+
"https://api.patentsview.org",
15+
"https://api.nsf.gov",
16+
"https://api.ies.ed.gov",
17+
"https://ies.ed.gov",
18+
"https://taggs.hhs.gov",
19+
"https://api.elsevier.com",
20+
"https://dev.elsevier.com",
21+
"https://ws.isiknowledge.com"
22+
]
23+
self.failed_hosts = []
24+
25+
def check_host_accessibility(self, host):
26+
with settings(warn_only=True, user=env.deploy_user):
27+
result = run(f"curl -s -o /dev/null -w '%{{http_code}}' -L {host}")
28+
http_code = result.stdout.strip()
29+
return http_code
30+
31+
def runTest(self):
32+
for host in self.hosts:
33+
http_code = self.check_host_accessibility(host)
34+
if http_code != "200":
35+
# Collect details about failed hosts for assertion later
36+
self.failed_hosts.append(f"{host} - HTTP Status: {http_code}")
37+
self.assertTrue(len(self.failed_hosts) == 0, "Some hosts were not accessible:\n" + "\n".join(self.failed_hosts))
38+
39+
def suite():
40+
suite = unittest.TestSuite()
41+
suite.addTest(CheckHostAccessibility())
42+
return suite
43+
44+
def main():
45+
runner = unittest.TextTestRunner()
46+
test_suite = suite()
47+
runner.run(test_suite)
48+

0 commit comments

Comments
 (0)