我应该如何对CMS进行泊坞管理,以使MySQL与git兼容?

发布时间:2020-07-07 15:20

我想通过dockerize一个MODX应用程序进行开发并将其存储在git中(再次用于开发。)有一个解决方案here,但是所有MySQL文件现在都是二进制文件,而且数据库关心它们的权限。我想要么

  • 将所有mysql的数据放入单个大型二进制文件中,因此我不必关心权限,可以将其放入LFS或
  • 在容器关闭时以某种方式将数据库导出到SQL文件,并在启动时将其导入,因此我可以使用diffs。
回答1

所以我实际上已经为您的问题实施了部分解决方案(虽然我仍在学习使用 docker,但这个潜在的解决方案封装了其他一切)。

我使用 MODx 作为我选择的 CMS,但是,理论上这也适用于其他 CMS。

在我的 git 工作流程中,我有一个预提交钩子,用于将数据库 mysqldump 到一系列 SQL 文件中,当在生产中实现时,这些文件代表 mysql 的输入以重新创建整个数据库。

下面例子中的一些代码与答案没有直接关系,另外值得注意的是,我个人在数据库的每个表中实现了最后一列,实际上将不同的数据行分离到不同的分支中git repo(因为我选择的工作流程涉及 3 个并行分支,每个分支分别用于本地开发、暂存和生产)。

下面的示例代码是我一个旧项目的预提交钩子,我不再使用它,但相同的代码仍然在使用中(除了一些与本文无关的例外)。它远远超出了问题,因为它是我的回购中的逐字逐句,但也许它可能会激发一些灵感。

在此示例中,您还将看到对“列表”的引用,这些列表是包含各种单独存储库和一些设置的文本文件,它们被内爆到 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 $1}')

# 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 $1}')
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) 我的代码从每个表中删除了所有 auto_increment 游标,因为它们在 sql 文件中创建了许多不必要的更改,最终使提交变得更加复杂。 (2) 我的代码也去掉了数据库名称本身,因为在生产服务器上,我将指定将使用的数据库,它与我用于本地开发的数据库名称不同,我们不想要数据去错误的地方。 (3) 此工作流程还将数据库结构和我提交给 git 的文件中的数据本身分开,如果您还没有意识到这一点,这可能会造成混淆。

另一方面,在服务器上实现项目时,我也有代码可以直观地遍历所有 *.sql 文件并将它们一次一个导入到我的数据库中。出于安全原因,我不会分享确切的代码,但一般要点是... mysql mysql_database < database_file.sql