timfeirg / lain-cli

DevOps with minimum effort.
https://lain-cli.readthedocs.io/en/latest/
MIT License
31 stars 9 forks source link

prepare.keep bug #16

Closed timfeirg closed 2 years ago

timfeirg commented 2 years ago

众所周知, lain prepare 的末尾, 会删除当前目录下的所有内容, 避免新老代码共存, 产生不可预计的影响. 但有些 prepare 的产物(缓存)我们不希望删除, 比如 ./node_modules, 这玩意删了的话, prepare 就相当于白做了. 因此才需要 prepare.keep 来声明保留哪些文件.

但现在看来, 删除当前目录下所有文件, 但保留一些特例, 是一个很难做好的事情, 在这里继续(再次)梳理下:

方案一: ls, grep, rm

最开始, keep 是这样实现的:

RUN ls -A1 {% if values.build.prepare.keep %} {% for k in values.build.prepare.keep %}| grep -v '\b{{ k }}\b' {% endfor %} {% endif %} | xargs rm -rf

这样的问题是, 不支持路径. 比如写了 foo/bar, 就搞不定了.

方案二: 清理前挪走, 事后再挪回来

类似这样:

    RUN rm -rf /tmp/* && mv treasure.txt /tmp/ &&  ls -A1 | xargs rm -rf && mv /tmp/treasure.txt .

这样的问题也很明显, 文件多了的话性能很差, 一个大的 node_modules, 要来回 move 两次的时间可不是开玩笑的. 容器里也不支持 link, 所以似乎没什么优化的机会.

方案三: find -delete

也就是这样:

RUN find . -type f {% if values.build.prepare.keep %}{% for k in values.build.prepare.keep %}{% if not loop.first %} -and {% endif %} -not -path '{{ k }}' {% endfor %}{% endif %} -delete

这个和方案一有着互补的问题, 比如说写了 node_modules/node_modules, 但事后 node_modules/node_modules 虽然保留了, node_modules/node_modules/foo 却被删了.

timfeirg commented 2 years ago

决定回滚方案一, 但不再支持 path, 只能写文件名. 也就是项目目录下的文件名.

timfeirg commented 2 years ago

cc @hongqn @kxxoling

hongqn commented 2 years ago
❯ tree
.
├── a
├── b
│   └── c
└── node_modules
    └── node_modules
        ├── bar
        │   └── baz
        └── foo

4 directories, 4 files
❯ find . -not -path './node_modules/node_modules' -and -not -path './node_modules/node_modules/*' -delete
find: cannot delete ‘./node_modules’: Directory not empty
❯ tree
.
└── node_modules
    └── node_modules
        ├── bar
        │   └── baz
        └── foo

3 directories, 2 files

看看这种可以吗?

timfeirg commented 2 years ago

你的示范应该是在建议, 继续沿用方案三(也就是 find -delete), 但会对 keep list 做 expansion, 比方说:

build:
  prepare:
    keep:
    - a
    - b/b

会被展开成为:

RUN find . -not -path './a' -and -not -path './a/*' -and -not -path './b/b' -and -not -path './b/b/*' -delete

这个方法确实好, 感谢建议, 我接下来按照这个示范来修复. cc @kxxoling