2024年Clojure状态调查!分享您的想法。

欢迎!请参阅关于页面以获取更多有关这一工作方式的信息。

+46
tools.deps
重定标签

拥有一个可以启用其他别名的别名将非常有用。

一个用例是有时针对一个项目进行开发,但有时针对项目集合进行开发。

以下是一个例子

{:aliases 
  {:dev/project1 {:override-deps {:local/root "/some/path/project1"}}
   :dev/project2 {:override-deps {:local/root "/some/path/project2"}}
   :dev/project3 {:override-deps {:local/root "/some/path/project3"}}
   ;; something like the following:
   :dev/all-projects {:aliases #{:dev/project1 :dev/project2 :dev/project3}}}

6个答案

+2

(在Slack讨论中询问https://clojure.atlassian.net/browse/CLJ-2638的状态之后)

一开始你可能认为一个别名要求其他别名可能不是特别有用,但我相信这是一种强大的结构,可以以简单的组织Clojure monorepos。当然,有很多处理Clojure monorepos问题的方法,但一种简单的方法是

  • 在仓库根目录中维护单个deps.edn
  • 每个外部依赖一项别名
  • 每个内部“模块”和关注点(如:foo/main:foo/test:bar/main:bar/test,…)一项别名

这给我们

  • 所有内容都在一个清晰的deps.edn
  • 无需动态构建deps.edn文件,就像某些项目中看到的那样(违背了声明式工具的使用目的)
  • 外部依赖已经锁定;例如,所有需要外部库的模块都使用相同的版本,不重复使用该库的专用别名
  • 非常容易构建所需的精确类路径;只需简单拼接别名
  • 没有类似递归本地依赖未更新的情况
  • 没有使用类似../../local-lib/src路径的不当做法

这是一个非常精确的方法,效果非常好。唯一的缺点是你现在必须处理许多别名,而且并不总是明显需要什么或不需什么

clj -M:module-1/dev:module-1:main:module-2/main:ext/lib-1:ext/lib-2:ext/lib-3...
当然可以编写调用过程脚本来处理成千上万的别名,但这过于复杂且容易出错。一个别名能够要求别名组将解决这个问题,个案已驳回。

by
另一个好处是它与CI和操控行为兼容,因为它可以防止在添加别定时更新无数脚本(即你总是可以在任何地方使用相同的“主”别名,并添加/删除它需要的别名,这在外部`deps.edn`中是不可见的)。
by
我们有一个monorepo,这绝对是我们痛点之一,直到我们在某个更适合deps.edn的结构上找到立足点——有关这一征程的系列博客可在corfield.org查到——现在我们正在向Polylith迁移,这只需要几个别名。话虽如此,我自己的开发环境依赖于这样的调用

SOCKET_REPL_PORT=5000 clojure -J-Dlog4j2.configurationFile=log4j2-sean.properties -M:rebel:portal:everything:dev:test:runner:build:add-libs:dev/repl

当然,非常希望能够将所有这些封装在一个:dev/sean别名中(我通常会为此列表添加一些其他别名以处理特定任务)。
+1
by

感谢John Stevenson指出,从多个别名继承时可能存在冲突的潜在风险。因此,使用别名向量而不是一组别名可能是更好的选择。由于这个担忧,可能还需要解决其他设计挑战,但我仍然认为这是一个有用的功能。

by
是的,排序确实很重要
+1
by

我的担忧是,把继承(甚至多重继承)的复杂性引入到原本简单的配置中。我从多年的Java经验中学到的一点是避免这些方法,而使用组合,这提供了更大的灵活性。

使用tools.build来解决这个问题是否会更简单一些?

在原始问题中提到的示例中,是否会有一种机制来防止或至少警告:dev/project1包含:dev/all-projects或其他包含别名的别名组的情况?这种分组可以有多少层级?

是否有明确的优先级规则?
是否会有工具来诊断冲突所在——Stree可能足够了,但我不确定

别名分组是否仅限于在项目的deps.edn中定义的别名,用户级别的deps.edn也包含在内(我假设排除用户级配置会增加更多复杂性)

也许有一种方法可以避免这种情况,那就是有一个类似于:meta-aliases的东西,它只能包含别名而不能包含其他:meta-aliases,但这样似乎还是在增加复杂性。

我希望能解决针对mono-repls的具体问题,而不需要在deps.edn配置中增加复杂性(特别是避免继承甚至多重继承)。

+1
by

我认为可能适用的语法类似于Leiningen的语法。

{:aliases
 {:with-tests {:extra-paths ["test"]}
  :with-dev-deps {:extra-deps [...]}
  :dev [:with-test :with-dev-deps {:exec-fn some/fn}]}}

:aliases中的别名可以是像我们已支持的普通映射,也可以是一个命名别名的别名向量,后面可以可选地跟一个映射。别名向量可以简视为包含那些别名的等价物,例如,上文示例中的dev别名基本上等同于with-tests:with-dev-deps:dev(使用dev的映射部分)

0
by
0
by

这将为代码检查非常有用。

clj -M:eastwood:kibit:antq

可能是

clj -M:lint
by
当你组合所有包含 :main-opts 的别名时,只使用最后一个(因为 main opts 是位置参数,传递给 clojure.main 的 -main 函数),因此您不能通过单个命令行运行多个代码检查工具,即使是使用多个别名。

您需要一些其他进程来依次运行三个代码检查工具的 -main 函数。
by
是的,这是一个众所周知的问题,但仍然可以通过在 ':lint' 别名内部提供一个自定义的 :main-opts 来克服这个问题,该 :main-opts 将所有工具的主 fn 调用组合成一步。例如,按照 Cam Saul 的 Lein-like 语法示例。这样它将是最后一个,并应覆盖其余内容。
马克,你是指包含"-e"的":main-opts"以及一个调用三个主函数的表达式吗?例如"(do (eastwood/-main) (kibit/-main) (antq/-main))"
(无论主命名空间是什么)。

鉴于许多工具在结束时调用(shutdown-agents),您可能无法在此之后运行主函数...
...