Я хочу изменить файлы pom.xml
на лету, чтобы Maven использовал локально установленные JAR-файлы для определенного groupId
. Итак, мне нужно:
system
.systemPath
к каждой соответствующей зависимости, указав на файл, имя которого будет функцией artifactId
.Например,
<dependency>
<groupId>myGroup</groupId>
<artifactId>myGroup-agent-api</artifactId>
<version>3.1.38.13</version>
<scope>provided</scope>
</dependency>
необходимо стать:
<dependency>
<groupId>myGroup</groupId>
<artifactId>myGroup-agent-api</artifactId>
<version>3.1.38.13</version>
<scope>system</scope>
<systemPath>${application_path}/jar/agent-api.jar</systemPath>
</dependency>
С первой частью я разобрался. Эта задача Ansible с использованием модуля xml должна изменять/устанавливать область действия:
- name: Set scope to system for myGroup if {{ module.name }} uses any
xml:
path: '{{ app_path }}/{{ module.name }}/pom.xml'
namespaces:
x: http://maven.apache.org/POM/4.0.0
xpath: '//x:dependency/x:groupId[text() = "myGroup"]/../x:scope'
value: system
register: xmlfound
when: java_files.matched
failed_when:
- xmlfound is failed
- >-
'in order to spawn nodes' not in xmlfound.msg
(Возиться с failed_when
необходимо, потому что lxml бросает вызов xpath, если не может найти соответствующие зависимости.)
Но как мне достичь второй части — создания systemPath
на основе artifactId
каждой соответствующей зависимости?
Обновление: отвечая на вопросы в комментариях:
pom.xml
и вызывает Maven — локально, на каждом сервере, предназначенном для запуска этих программ.add_children
, но какой будет аргумент? systemPath
каждой зависимости различен и является производным от artifactId
.xpath
. К сожалению, он по-прежнему вызывает ошибки для POM без единой соответствующей зависимости, поэтому мне все еще нужен хакерский подход failed_when
...🤔 А знаете ли вы, что...
XML может быть преобразован в другие форматы данных с использованием технологии XSLT (Extensible Stylesheet Language Transformations).
Я бы предпочел решение Maven Profiles, упомянутое Александр Плетнев в комментарии к вопросу , поскольку это делает ваши сборки более независимыми от внешних инструментов/конфигураций:
Прежде всего, я бы сохранил вашу зависимость myGroup-agent-api
в вашем:
например, возможность извлечь выгоду из обычного разрешения зависимостей Maven.
Во-вторых, в вашем POM:
<properties>
<myGroup-agent-api.version>3.1.38.13</myGroup-agent-api.version>
<properties>
...
<profiles>
<profile>
<id>agent-provided</id>
<!-- See https://maven.apache.org/guides/introduction/introduction-to-profiles.html#details-on-profile-activation
"All profiles that are active by default are automatically
deactivated when a profile in the POM is activated on the
command line or through its activation config."
-->
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>myGroup</groupId>
<artifactId>myGroup-agent-api</artifactId>
<version>${myGroup-agent-api.version}</version>
<scope>provided</scope>
</dependency>
<dependencies>
</profile>
<profile>
<id>agent-repo</id>
<dependencies>
<dependency>
<groupId>myGroup</groupId>
<artifactId>myGroup-agent-api</artifactId>
<version>${myGroup-agent-api.version}</version>
</dependency>
<dependencies>
</profile>
</profiles>
...
Запустите mvn <phase>
для сборки с зависимостями <scope>provided
.
Запустите mvn <phase> -P agent-repo
для сборки с разрешенной зависимостью из вашего репозитория. При этом профиль agent-provided
будет деактивирован; см. комментарий в декларации POM выше.
Отказ от ответственности: это только ответ на исходный вопрос. Чтобы решить эту проблему, используйте вместо этого профили Maven.
Рассмотрим следующий pom.xml
пример:
<project xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>your-group</groupId>
<artifactId>your-artifact</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>myGroup</groupId>
<artifactId>myGroup-agent-api</artifactId>
<version>3.1.38.13</version>
<classifier>yaml</classifier>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.mojo</groupId>
<artifactId>my-artifact</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>myGroup</groupId>
<artifactId>myGroup-another-api</artifactId>
<version>3.5.79.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Но как мне достичь второй части — создания
systemPath
на основеartifactId
каждой соответствующей зависимости?
К сожалению, этот вариант использования кажется слишком сложным для модуля xml
.
Сначала вам нужно получить список этих зависимостей, и вот в чем проблема: список совпадений плоский. Таким образом, вам придется правильно воссоздать структуру, что является непростой задачей, особенно если ваши узлы dependency
имеют нечетное количество вложенных узлов. Поскольку я не использовал XPath в течение многих лет, возможно, я использую неправильный, но вот о чем я говорю (учитывая простейший pom.xml
, где 2 из 3 зависимостей находятся на артефактах с «myGroup» groupId
):
# playbook.yaml
---
- name: Modify the pom.xml
hosts: localhost
connection: local
gather_facts: false
tasks:
- name: Find the dependencies on myGroup modules
xml:
path: 'pom.xml'
namespaces:
x: http://maven.apache.org/POM/4.0.0
xpath: '//x:dependency/x:groupId[text() = "myGroup"]/..//*'
content: text
register: xmlfound
- name: Show the results
debug:
var: xmlfound
TASK [debug] **************************************************************************
ok: [localhost] =>
xmlfound:
actions:
namespaces:
x: http://maven.apache.org/POM/4.0.0
state: present
xpath: //x:dependency/x:groupId[text() = "myGroup"]/..//*
changed: false
count: 8
failed: false
matches:
- '{http://maven.apache.org/POM/4.0.0}groupId': myGroup
- '{http://maven.apache.org/POM/4.0.0}artifactId': myGroup-agent-api
- '{http://maven.apache.org/POM/4.0.0}version': 3.1.38.13
- '{http://maven.apache.org/POM/4.0.0}scope': provided
- '{http://maven.apache.org/POM/4.0.0}groupId': myGroup
- '{http://maven.apache.org/POM/4.0.0}artifactId': myGroup-another-api
- '{http://maven.apache.org/POM/4.0.0}version': 3.5.79.0
- '{http://maven.apache.org/POM/4.0.0}scope': provided
msg: 8
И я даже не говорю об переборе всех POM в проектах, вложенных циклах и о том, что вам нужно использовать модуль win_xml
, если у вас Windows-машины.
Вместо этого вы можете использовать фильтр ansible.utils.from_xml
для загрузки всего файла, а затем найти зависимости для обработки с помощью фильтра selectattr
Jinja:
# playbook.yaml
- name: Modify the pom.xml
hosts: localhost
connection: local
gather_facts: false
vars:
vendor_group_id: myGroup
tasks:
- name: Read the pom.xml
set_fact:
current_pom: "{{ lookup('file', 'pom.xml') | ansible.utils.from_xml }}"
- name: Detect the dependencies to process
set_fact:
dependencies_to_process: >-
{{
current_pom.project.dependencies.dependency
| selectattr('groupId', 'equalto', vendor_group_id)
}}
Следующий шаг — использовать add_elements
. Сложные моменты здесь заключаются в следующем:
pretty_print: true
помогает, но меняет отступ с 4 пробелов (типично для POM) на 2 пробела;systemPath
.Чтобы преодолеть это, вам необходимо:
artifactId
вместо узлов groupId
, перебирая список, построенный в предыдущей задаче;systemPath
только в том случае, если элемент списка его еще не содержит;xmllint
. Конечно, это можно (и вообще нужно) сделать с помощью встроенных модулей Ansible, таких как replace
, но в данном случае это была бы слишком сложная задача. Самым простым решением было бы использовать фильтр ansible.utils.to_xml
, но при этом комментарии, если они были, будут потеряны.Вот почти (см. примечания ниже) полный идемпотентный пример (обратите внимание, что я использовал lstrip(vendor_group_id + "-")
для определения пути — в вашем случае он может отличаться):
- name: Modify the pom.xml
hosts: localhost
connection: local
gather_facts: false
vars:
vendor_group_id: myGroup
tasks:
- name: Read the pom.xml
set_fact:
current_pom: "{{ lookup('file', 'pom.xml') | ansible.utils.from_xml }}"
- name: Replace the scope
set_fact:
dependencies_to_process: >-
{{
current_pom.project.dependencies.dependency
| selectattr('groupId', 'equalto', vendor_group_id)
}}
- name: Set scope to system for myGroup if uses any
xml:
path: 'pom.xml'
namespaces:
x: http://maven.apache.org/POM/4.0.0
xpath: '//x:dependency[x:groupId[text() = "{{ vendor_group_id }}"]]/x:scope'
value: system
- name: Set systemPath for myGroup
xml:
path: 'pom.xml'
namespaces:
x: http://maven.apache.org/POM/4.0.0
xpath: '//x:dependency[x:artifactId[text() = "{{ item.artifactId }}"]]'
pretty_print: true
add_children:
- systemPath: >-
${application_path}/jar/{{ item.artifactId.lstrip(vendor_group_id + "-") }}.jar
loop: '{{ dependencies_to_process }}'
register: add_children_result
when: item.systemPath is not defined
- name: Restore the indentation
environment:
XMLLINT_INDENT: ' '
command: 'xmllint pom.xml --output pom.xml --format'
when: add_children_result is defined and add_children_result.changed
ПРИМЕЧАНИЯ:
Возиться с
failed_when
необходимо, потому чтоlxml
вызывает раздражение у xpath, если он не может найти соответствующие зависимости.
Я бы не сказал «необходимо», поскольку вы можете обработать список соответствующих зависимостей, как я предложил. Более того, вы наверняка захотите добавить scope
, если ни одного набора не было.
Важно: ради простоты я даже не учел dependencyManagement
. Как вы можете видеть, это решение уже слишком сложно даже для простого случая.