我应该如何对 CMS 进行 docker 化,以便 MySQL 与 git 一起工作?
How should I dockerize a CMS, such that MySQL works nice with git?
我想将一个 MODX 应用程序 dockerize 用于开发并将其存储在 git 中(同样,用于开发。)有一个解决方案 here,但是所有 MySQL 文件现在都在二进制中,再加上数据库关心他们的权限。我也想
- 将 mysql 的所有数据放入一个大型二进制文件中,这样我就不必关心权限,我可以将其放入 LFS 或
- 以某种方式在容器关闭时将数据库导出到 SQL 文件并在启动时导入它,这样我就可以使用 diffs。
所以我实际上已经对您的问题实施了部分解决方案(尽管我仍在学习使用 docker,这个潜在的解决方案封装了其他所有内容)。
我使用 MODx 作为我选择的 CMS,但是,理论上这也适用于其他 CMS。
在我的 git 工作流程中,我有一个预提交挂钩设置为 mysql 将数据库转储到一系列 SQL 文件中,这些文件在生产中实现时代表输入进入 mysql 以重新创建整个数据库。
以下示例中的一些代码与答案没有直接关系,同样值得注意的是,我个人在数据库的每个 table 中实现了一个最终列,它实际上将不同的行分开数据到 git 存储库的不同分支(因为我选择的工作流程涉及 3 个并行分支,每个分支分别用于本地开发、暂存和生产)。
下面的示例代码是我的一个旧项目的预提交挂钩,我不再使用它,但相同的代码仍在使用(除了一些与此无关的例外 post ).它远远超出了问题的范围,因为它是我的回购协议中的逐字记录,但也许它可能会激发一些灵感。
在此示例中,您还会看到对“列表”的引用,它们是包含各种个人存储库和一些设置的文本文件,它们被分解为 bash 关联数组,这需要 bash 4.0 或更高版本。还有对 'mysql-defaults' 的引用,这是一个包含我的数据库凭据的文本文件,因此脚本可以 运行 不间断。
#!/bin/bash
# Set repository and script variables
REPO_NAME='MODX';REPO_FOLDER='modx';REPO_KEY='modx';REPO_TYPE='MODX';
declare -a REPO_PREFIX_COUNTS=();
MODULES_STRING=$(cat /Users/cjholowatyj/Dev/modules-list | tr "\n" " ");MODULES_ARRAY=(${MODULES_STRING});# echo ${MODULES_ARRAY[1]};
PROJECTS_STRING=$(cat /Users/cjholowatyj/Dev/projects-list | tr "\n" " ");PROJECTS_ARRAY=(${PROJECTS_STRING});# echo ${PROJECTS_ARRAY[1]};
THEMES_STRING=$(cat /Users/cjholowatyj/Dev/themes-list | tr "\n" " ");THEMES_ARRAY=(${THEMES_STRING});# echo ${THEMES_ARRAY[1]};
alias mysql='/Applications/MAMP/Library/bin/mysql --defaults-file=.git/hooks/mysql-defaults';
alias dump='/Applications/MAMP/Library/bin/mysqldump --defaults-file=.git/hooks/mysql-defaults';
alias dump-compact='/Applications/MAMP/Library/bin/mysqldump --defaults-file=.git/hooks/mysql-defaults --no-create-info --skip-add-locks --skip-disable-keys --skip-comments --skip-extended-insert --compact';
shopt -s expand_aliases
# Print status message in terminal console
/bin/echo "Running ${REPO_NAME} Pre-Commits...";
# Switch to repository directory
# shellcheck disable=SC2164
cd "/Users/cjholowatyj/Dev/${REPO_FOLDER}/";
# Fetch database tables dedicated to this repository
mysql -N information_schema -e "select table_name from tables where table_schema = 'ka_local2019' and table_name like '${REPO_KEY}_%'" | tr '\n' ' ' > sql/${REPO_KEY}_tables.txt;
tablesExist=$(wc -c "sql/${REPO_KEY}_tables.txt" | awk '{print }')
# Reset pack_ sql files
if [[ -f sql/pack_structure.sql ]]; then rm sql/pack_structure.sql; fi
if [[ -f sql/pack_data.sql ]]; then rm sql/pack_data.sql; fi
touch sql/pack_structure.sql
touch sql/pack_data.sql
dump --add-drop-database --no-create-info --no-data --skip-comments --databases ka_local2019 >> sql/pack_structure.sql
# Process repository tables & data
if [[ ${tablesExist} -gt 0 ]]; then
dump --no-data --skip-comments ka_local2019 --tables `cat sql/${REPO_KEY}_tables.txt` >> sql/pack_structure.sql
dump-compact ka_local2019 --tables `cat sql/${REPO_KEY}_tables.txt` --where="flighter_key IS NULL" >> sql/pack_data.sql
sed -i "" "s/AUTO_INCREMENT=[0-9]+[ ]//g" sql/pack_structure.sql
fi
dump-compact ka_local2019 --where="flighter_key='${REPO_KEY}'" >> sql/pack_data.sql
isLocalHead=$(grep -c cjholowatyj .git/HEAD);
if [[ ${isLocalHead} = 1 ]]; then
dump-compact ka_local2019 --where="flighter_key='${REPO_KEY}-local'" >> sql/pack_data.sql
sed -i "" "s/\.\[${REPO_KEY}-local]//g" sql/pack_data.sql
fi
isDevelopHead=$(grep -c develop .git/HEAD);
if [[ ${isDevelopHead} = 1 ]]; then
dump-compact ka_local2019 --where="flighter_key='${REPO_KEY}-develop'" >> sql/pack_data.sql
sed -i "" "s/\.\[${REPO_KEY}-develop]//g" sql/pack_data.sql
sed -i "" "s/ka_local2019/ka_dev2019/g" sql/pack_structure.sql
sed -i "" "s/ka_local2019/ka_dev2019/g" sql/pack_structure.sql
fi
isReleaseHead=$(grep -c release .git/HEAD);
if [[ ${isReleaseHead} = 1 ]]; then
dump-compact ka_local2019 --where="flighter_key='${REPO_KEY}-release'" >> sql/pack_data.sql
sed -i "" "s/\.\[${REPO_KEY}-release]//g" sql/pack_data.sql
sed -i "" "s/ka_local2019/ka_rel2019/g" sql/pack_structure.sql
sed -i "" "s/ka_local2019/ka_rel2019/g" sql/pack_structure.sql
fi
# Create master structure sql file for this repository (and delete it once again if it is empty)
awk '/./ { e=0 } /^$/ { e += 1 } e <= 1' < sql/pack_structure.sql > sql/${REPO_KEY}_structure.sql
structureExists=$(wc -c "sql/${REPO_KEY}_structure.sql" | awk '{print }')
if [[ ${structureExists} -eq 0 ]]; then rm sql/${REPO_KEY}_structure.sql; fi
# Create master sql data file in case the entire database needs to be rebuilt from scratch
awk '/./ { e=0 } /^$/ { e += 1 } e <= 1' < sql/pack_data.sql > sql/all_${REPO_KEY}_data.sql
# Commit global repository sql files
git add sql/all_${REPO_KEY}_data.sql
if [[ ${structureExists} -gt 0 ]]; then git add sql/${REPO_KEY}_structure.sql; fi
# Deleting any existing sql files to recreate them fresh below
if [[ -f sql/create_modx_data.sql ]]; then rm sql/create_modx_data.sql; fi
if [[ -f sql/create_flighter_data.sql ]]; then rm sql/create_flighter_data.sql; fi
for i in "${MODULES_ARRAY[@]}"
do
if [[ -f sql/create_${i}_data.sql ]]; then rm sql/create_${i}_data.sql; fi
done
if [[ -f sql/create_${REPO_KEY}_data.sql ]]; then rm sql/create_${REPO_KEY}_data.sql; fi
# Parse global repository data and separate out data filed by table prefix
lastPrefix='';
lastTable='';
while IFS= read -r iLine;
do
thisLine="${iLine}";
thisPrefix=$(echo ${thisLine} | grep -oEi '^INSERT INTO `([0-9a-zA-Z]+)_' | cut -d ' ' -f 3 | cut -d '`' -f 2 | cut -d '_' -f 1);
thisTable=$(echo ${thisLine} | grep -oEi '^INSERT INTO `([0-9a-zA-Z_]+)`' | cut -d ' ' -f 3 | cut -d '`' -f 2);
if [[ $(echo -n ${thisPrefix} | wc -m) -gt 0 ]]; then
if [[ -n "${REPO_PREFIX_COUNTS[$thisPrefix]}" ]]; then
if [[ ${REPO_PREFIX_COUNTS[$thisPrefix]} -lt 1 ]]; then
if [[ -f sql/create_${thisPrefix}_data.sql ]]; then rm sql/create_${thisPrefix}_data.sql; fi
touch "sql/create_${thisPrefix}_data.sql";
fi
REPO_PREFIX_COUNTS[$thisPrefix]=0;
fi
REPO_PREFIX_COUNTS[$thisPrefix]+=1;
echo "${thisLine}" >> sql/create_${thisPrefix}_data.sql;
if [[ ${thisTable} != ${lastTable} ]]; then
if [[ ${thisPrefix} != ${lastPrefix} ]]; then
if [[ -f sql/delete_${thisPrefix}_data.sql ]]; then rm sql/delete_${thisPrefix}_data.sql; fi
touch "sql/delete_${thisPrefix}_data.sql";
fi
if [[ $(echo -n ${thisTable} | wc -m) -gt 0 ]]; then
echo "DELETE FROM \`${thisTable}\` WHERE \`flighter_key\` LIKE '${REPO_KEY}%';" >> sql/delete_${thisPrefix}_data.sql
fi
fi
# Add previous prefix sql file to git if lastPrefix isn't ''
if [[ $(echo -n ${lastPrefix} | wc -m) -gt 0 ]]; then
git add "sql/create_${lastPrefix}_data.sql";
git add "sql/delete_${lastPrefix}_data.sql";
fi
fi
lastPrefix=${thisPrefix};
lastTable=${thisTable};
done < sql/all_${REPO_KEY}_data.sql
# Add previous prefix sql file to git for the final lastPrefix value
git add "sql/create_${lastPrefix}_data.sql";
git add "sql/delete_${lastPrefix}_data.sql";
# Clean up unused files
rm "sql/${REPO_KEY}_tables.txt";
rm "sql/pack_data.sql";
rm "sql/pack_structure.sql";
git add sql/;
一些值得注意的细微差别是... (1) 我的代码从每个 table 中删除了所有 auto_increment 游标,因为它们在 [=41] 中创建了很多不必要的更改=] 文件,最终使提交变得更加复杂。 (2) 我的代码也删除了数据库名称本身,因为在生产服务器上,我将指定将要使用的数据库,它与我用于本地开发的数据库名称不同,我们不这样做希望数据去错地方。 (3) 此工作流还将数据库结构和我提交给 git 的文件中的数据本身分开,如果您还没有意识到这一点,这可能会造成混淆。
另一方面,在服务器上实施项目时,我也有直观地遍历所有 *.sql 文件并将它们导入的代码我的数据库一次一个。出于安全原因,我不会分享确切的代码,但总的要点是...... mysql mysql_database < database_file.sql
我想将一个 MODX 应用程序 dockerize 用于开发并将其存储在 git 中(同样,用于开发。)有一个解决方案 here,但是所有 MySQL 文件现在都在二进制中,再加上数据库关心他们的权限。我也想
- 将 mysql 的所有数据放入一个大型二进制文件中,这样我就不必关心权限,我可以将其放入 LFS 或
- 以某种方式在容器关闭时将数据库导出到 SQL 文件并在启动时导入它,这样我就可以使用 diffs。
所以我实际上已经对您的问题实施了部分解决方案(尽管我仍在学习使用 docker,这个潜在的解决方案封装了其他所有内容)。
我使用 MODx 作为我选择的 CMS,但是,理论上这也适用于其他 CMS。
在我的 git 工作流程中,我有一个预提交挂钩设置为 mysql 将数据库转储到一系列 SQL 文件中,这些文件在生产中实现时代表输入进入 mysql 以重新创建整个数据库。
以下示例中的一些代码与答案没有直接关系,同样值得注意的是,我个人在数据库的每个 table 中实现了一个最终列,它实际上将不同的行分开数据到 git 存储库的不同分支(因为我选择的工作流程涉及 3 个并行分支,每个分支分别用于本地开发、暂存和生产)。
下面的示例代码是我的一个旧项目的预提交挂钩,我不再使用它,但相同的代码仍在使用(除了一些与此无关的例外 post ).它远远超出了问题的范围,因为它是我的回购协议中的逐字记录,但也许它可能会激发一些灵感。
在此示例中,您还会看到对“列表”的引用,它们是包含各种个人存储库和一些设置的文本文件,它们被分解为 bash 关联数组,这需要 bash 4.0 或更高版本。还有对 'mysql-defaults' 的引用,这是一个包含我的数据库凭据的文本文件,因此脚本可以 运行 不间断。
#!/bin/bash
# Set repository and script variables
REPO_NAME='MODX';REPO_FOLDER='modx';REPO_KEY='modx';REPO_TYPE='MODX';
declare -a REPO_PREFIX_COUNTS=();
MODULES_STRING=$(cat /Users/cjholowatyj/Dev/modules-list | tr "\n" " ");MODULES_ARRAY=(${MODULES_STRING});# echo ${MODULES_ARRAY[1]};
PROJECTS_STRING=$(cat /Users/cjholowatyj/Dev/projects-list | tr "\n" " ");PROJECTS_ARRAY=(${PROJECTS_STRING});# echo ${PROJECTS_ARRAY[1]};
THEMES_STRING=$(cat /Users/cjholowatyj/Dev/themes-list | tr "\n" " ");THEMES_ARRAY=(${THEMES_STRING});# echo ${THEMES_ARRAY[1]};
alias mysql='/Applications/MAMP/Library/bin/mysql --defaults-file=.git/hooks/mysql-defaults';
alias dump='/Applications/MAMP/Library/bin/mysqldump --defaults-file=.git/hooks/mysql-defaults';
alias dump-compact='/Applications/MAMP/Library/bin/mysqldump --defaults-file=.git/hooks/mysql-defaults --no-create-info --skip-add-locks --skip-disable-keys --skip-comments --skip-extended-insert --compact';
shopt -s expand_aliases
# Print status message in terminal console
/bin/echo "Running ${REPO_NAME} Pre-Commits...";
# Switch to repository directory
# shellcheck disable=SC2164
cd "/Users/cjholowatyj/Dev/${REPO_FOLDER}/";
# Fetch database tables dedicated to this repository
mysql -N information_schema -e "select table_name from tables where table_schema = 'ka_local2019' and table_name like '${REPO_KEY}_%'" | tr '\n' ' ' > sql/${REPO_KEY}_tables.txt;
tablesExist=$(wc -c "sql/${REPO_KEY}_tables.txt" | awk '{print }')
# Reset pack_ sql files
if [[ -f sql/pack_structure.sql ]]; then rm sql/pack_structure.sql; fi
if [[ -f sql/pack_data.sql ]]; then rm sql/pack_data.sql; fi
touch sql/pack_structure.sql
touch sql/pack_data.sql
dump --add-drop-database --no-create-info --no-data --skip-comments --databases ka_local2019 >> sql/pack_structure.sql
# Process repository tables & data
if [[ ${tablesExist} -gt 0 ]]; then
dump --no-data --skip-comments ka_local2019 --tables `cat sql/${REPO_KEY}_tables.txt` >> sql/pack_structure.sql
dump-compact ka_local2019 --tables `cat sql/${REPO_KEY}_tables.txt` --where="flighter_key IS NULL" >> sql/pack_data.sql
sed -i "" "s/AUTO_INCREMENT=[0-9]+[ ]//g" sql/pack_structure.sql
fi
dump-compact ka_local2019 --where="flighter_key='${REPO_KEY}'" >> sql/pack_data.sql
isLocalHead=$(grep -c cjholowatyj .git/HEAD);
if [[ ${isLocalHead} = 1 ]]; then
dump-compact ka_local2019 --where="flighter_key='${REPO_KEY}-local'" >> sql/pack_data.sql
sed -i "" "s/\.\[${REPO_KEY}-local]//g" sql/pack_data.sql
fi
isDevelopHead=$(grep -c develop .git/HEAD);
if [[ ${isDevelopHead} = 1 ]]; then
dump-compact ka_local2019 --where="flighter_key='${REPO_KEY}-develop'" >> sql/pack_data.sql
sed -i "" "s/\.\[${REPO_KEY}-develop]//g" sql/pack_data.sql
sed -i "" "s/ka_local2019/ka_dev2019/g" sql/pack_structure.sql
sed -i "" "s/ka_local2019/ka_dev2019/g" sql/pack_structure.sql
fi
isReleaseHead=$(grep -c release .git/HEAD);
if [[ ${isReleaseHead} = 1 ]]; then
dump-compact ka_local2019 --where="flighter_key='${REPO_KEY}-release'" >> sql/pack_data.sql
sed -i "" "s/\.\[${REPO_KEY}-release]//g" sql/pack_data.sql
sed -i "" "s/ka_local2019/ka_rel2019/g" sql/pack_structure.sql
sed -i "" "s/ka_local2019/ka_rel2019/g" sql/pack_structure.sql
fi
# Create master structure sql file for this repository (and delete it once again if it is empty)
awk '/./ { e=0 } /^$/ { e += 1 } e <= 1' < sql/pack_structure.sql > sql/${REPO_KEY}_structure.sql
structureExists=$(wc -c "sql/${REPO_KEY}_structure.sql" | awk '{print }')
if [[ ${structureExists} -eq 0 ]]; then rm sql/${REPO_KEY}_structure.sql; fi
# Create master sql data file in case the entire database needs to be rebuilt from scratch
awk '/./ { e=0 } /^$/ { e += 1 } e <= 1' < sql/pack_data.sql > sql/all_${REPO_KEY}_data.sql
# Commit global repository sql files
git add sql/all_${REPO_KEY}_data.sql
if [[ ${structureExists} -gt 0 ]]; then git add sql/${REPO_KEY}_structure.sql; fi
# Deleting any existing sql files to recreate them fresh below
if [[ -f sql/create_modx_data.sql ]]; then rm sql/create_modx_data.sql; fi
if [[ -f sql/create_flighter_data.sql ]]; then rm sql/create_flighter_data.sql; fi
for i in "${MODULES_ARRAY[@]}"
do
if [[ -f sql/create_${i}_data.sql ]]; then rm sql/create_${i}_data.sql; fi
done
if [[ -f sql/create_${REPO_KEY}_data.sql ]]; then rm sql/create_${REPO_KEY}_data.sql; fi
# Parse global repository data and separate out data filed by table prefix
lastPrefix='';
lastTable='';
while IFS= read -r iLine;
do
thisLine="${iLine}";
thisPrefix=$(echo ${thisLine} | grep -oEi '^INSERT INTO `([0-9a-zA-Z]+)_' | cut -d ' ' -f 3 | cut -d '`' -f 2 | cut -d '_' -f 1);
thisTable=$(echo ${thisLine} | grep -oEi '^INSERT INTO `([0-9a-zA-Z_]+)`' | cut -d ' ' -f 3 | cut -d '`' -f 2);
if [[ $(echo -n ${thisPrefix} | wc -m) -gt 0 ]]; then
if [[ -n "${REPO_PREFIX_COUNTS[$thisPrefix]}" ]]; then
if [[ ${REPO_PREFIX_COUNTS[$thisPrefix]} -lt 1 ]]; then
if [[ -f sql/create_${thisPrefix}_data.sql ]]; then rm sql/create_${thisPrefix}_data.sql; fi
touch "sql/create_${thisPrefix}_data.sql";
fi
REPO_PREFIX_COUNTS[$thisPrefix]=0;
fi
REPO_PREFIX_COUNTS[$thisPrefix]+=1;
echo "${thisLine}" >> sql/create_${thisPrefix}_data.sql;
if [[ ${thisTable} != ${lastTable} ]]; then
if [[ ${thisPrefix} != ${lastPrefix} ]]; then
if [[ -f sql/delete_${thisPrefix}_data.sql ]]; then rm sql/delete_${thisPrefix}_data.sql; fi
touch "sql/delete_${thisPrefix}_data.sql";
fi
if [[ $(echo -n ${thisTable} | wc -m) -gt 0 ]]; then
echo "DELETE FROM \`${thisTable}\` WHERE \`flighter_key\` LIKE '${REPO_KEY}%';" >> sql/delete_${thisPrefix}_data.sql
fi
fi
# Add previous prefix sql file to git if lastPrefix isn't ''
if [[ $(echo -n ${lastPrefix} | wc -m) -gt 0 ]]; then
git add "sql/create_${lastPrefix}_data.sql";
git add "sql/delete_${lastPrefix}_data.sql";
fi
fi
lastPrefix=${thisPrefix};
lastTable=${thisTable};
done < sql/all_${REPO_KEY}_data.sql
# Add previous prefix sql file to git for the final lastPrefix value
git add "sql/create_${lastPrefix}_data.sql";
git add "sql/delete_${lastPrefix}_data.sql";
# Clean up unused files
rm "sql/${REPO_KEY}_tables.txt";
rm "sql/pack_data.sql";
rm "sql/pack_structure.sql";
git add sql/;
一些值得注意的细微差别是... (1) 我的代码从每个 table 中删除了所有 auto_increment 游标,因为它们在 [=41] 中创建了很多不必要的更改=] 文件,最终使提交变得更加复杂。 (2) 我的代码也删除了数据库名称本身,因为在生产服务器上,我将指定将要使用的数据库,它与我用于本地开发的数据库名称不同,我们不这样做希望数据去错地方。 (3) 此工作流还将数据库结构和我提交给 git 的文件中的数据本身分开,如果您还没有意识到这一点,这可能会造成混淆。
另一方面,在服务器上实施项目时,我也有直观地遍历所有 *.sql 文件并将它们导入的代码我的数据库一次一个。出于安全原因,我不会分享确切的代码,但总的要点是...... mysql mysql_database < database_file.sql