Packaging and Deployment
Overview
- What you’ll learn:
- How Eclipse P2 provisioning works and how to create Feature projects and Update Site projects for distributing iDempiere plugins
- How to build plugins with Maven/Tycho, set up continuous integration with GitHub Actions, and automate testing and deployment
- How to manage plugin versioning, dependencies, Docker-based deployment, and release management for production environments
- Prerequisites: Lesson 15 — Building Your First Plugin, Lesson 33 — Testing and Debugging Plugins, basic familiarity with Maven and Git
- Estimated reading time: 24 minutes
Introduction
A plugin that exists only on a developer’s workstation provides no business value. To be useful, plugins must be packaged, versioned, tested automatically, and deployed reliably to production servers. iDempiere leverages Eclipse’s P2 provisioning system for plugin distribution — the same technology that manages plugins in the Eclipse IDE itself. Combined with Maven/Tycho for builds and CI/CD pipelines for automation, you can establish a professional software delivery process for your iDempiere customizations.
This lesson covers the complete journey from source code to production deployment: organizing plugins into features, building P2 update sites, automating builds with continuous integration, managing versions and dependencies, and deploying to production environments including Docker containers.
The P2 Repository Concept
Eclipse P2 (Provisioning Platform) is a framework for installing, updating, and managing Eclipse-based applications and their plugins. iDempiere is built on Eclipse Equinox (an OSGi implementation), which makes P2 its native plugin distribution mechanism.
How P2 Works
A P2 repository is a structured directory (local or hosted on a web server) containing:
- Plugin JARs: The compiled OSGi bundles containing your code.
- Feature JARs: Metadata that groups related plugins into installable units.
- content.xml / content.jar: An index of all installable units (plugins and features) in the repository, including their versions and dependencies.
- artifacts.xml / artifacts.jar: An index of the physical artifact files (JARs) and their download locations.
When you install a plugin from a P2 repository, the P2 engine resolves all dependencies, downloads the required artifacts, and configures the OSGi runtime to include the new bundles. This ensures that plugins are installed with all their prerequisites satisfied.
P2 vs. Manual JAR Deployment
While you can deploy plugins by manually copying JAR files to the plugins/ directory, P2 provides critical advantages:
- Dependency resolution: P2 ensures all required bundles are present before installation.
- Version management: P2 tracks installed versions and can upgrade, downgrade, or uninstall cleanly.
- Atomic updates: P2 either installs everything successfully or rolls back, preventing partial installations.
- Multiple repositories: You can configure multiple P2 repositories (iDempiere core + your custom plugins) and P2 resolves dependencies across all of them.
Feature Projects
A Feature project groups one or more plugins into a single installable unit. Think of it as a “product package” — users install the feature, and all included plugins are deployed together.
Creating a Feature Project
- In Eclipse, select File > New > Project > Plug-in Development > Feature Project.
- Name it following the convention:
com.example.feature. - On the “Referenced Plug-ins” page, select all plugins that belong to this feature.
The feature.xml File
The feature.xml file defines the feature’s metadata and contents:
<?xml version="1.0" encoding="UTF-8"?>
<feature
id="com.example.feature"
label="Example iDempiere Customization"
version="1.0.0.qualifier"
provider-name="Example Corp">
<description>
Custom business logic and reports for Example Corp's
iDempiere deployment.
</description>
<copyright>
Copyright (c) 2025 Example Corp. All rights reserved.
</copyright>
<license url="https://www.example.com/license">
[License text here]
</license>
<!-- Required iDempiere features (dependencies) -->
<requires>
<import feature="org.adempiere.server" version="11.0.0" match="greaterOrEqual"/>
<import feature="org.adempiere.ui.zk" version="11.0.0" match="greaterOrEqual"/>
</requires>
<!-- Plugins included in this feature -->
<plugin
id="com.example.model"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.example.process"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.example.reports"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.example.rest"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
</feature>
Feature Organization Strategies
For large customizations, consider splitting into multiple features:
- com.example.core.feature: Model classes, validators, and core business logic used by all other components.
- com.example.ui.feature: UI customizations, dashboard gadgets, and custom forms (depends on core).
- com.example.reports.feature: JasperReports and report processes (depends on core).
- com.example.rest.feature: Custom REST API endpoints (depends on core).
This separation allows deploying only the components needed for a specific environment (e.g., the REST feature might only be deployed on the API server, not on the user-facing server).
Update Site Project
An Update Site project builds the P2 repository from your features and plugins.
Creating an Update Site Project
- In Eclipse, select File > New > Project > Plug-in Development > Update Site Project.
- Name it:
com.example.p2. - Open the
site.xmlfile and add your features.
The site.xml File
<?xml version="1.0" encoding="UTF-8"?>
<site>
<feature url="features/com.example.feature_1.0.0.qualifier.jar"
id="com.example.feature"
version="1.0.0.qualifier">
<category name="example-customization"/>
</feature>
<category-def name="example-customization"
label="Example Corp Customization">
<description>Custom plugins for Example Corp deployment</description>
</category-def>
</site>
Building the Update Site Manually
In Eclipse, open the site.xml editor and click Build All. Eclipse compiles all plugins and features, and generates the P2 repository artifacts in the update site project directory. The result is a directory structure like:
com.example.p2/
features/
com.example.feature_1.0.0.202501150900.jar
plugins/
com.example.model_1.0.0.202501150900.jar
com.example.process_1.0.0.202501150900.jar
com.example.reports_1.0.0.202501150900.jar
com.example.rest_1.0.0.202501150900.jar
content.jar
artifacts.jar
Building with Maven/Tycho
Manual Eclipse builds are fine for development, but automated builds require Maven with the Tycho plugin. Tycho teaches Maven to understand Eclipse plugin projects, feature projects, and P2 repositories.
Parent POM
Create a parent POM at the root of your project repository:
<?xml version="1.0" encoding="UTF-8"?>
<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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>com.example.parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<tycho.version>4.0.4</tycho.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<idempiere.target>https://download.idempiere.org/11/p2/</idempiere.target>
</properties>
<modules>
<module>com.example.model</module>
<module>com.example.process</module>
<module>com.example.reports</module>
<module>com.example.rest</module>
<module>com.example.model.test</module>
<module>com.example.feature</module>
<module>com.example.p2</module>
</modules>
<repositories>
<repository>
<id>idempiere</id>
<url>${idempiere.target}</url>
<layout>p2</layout>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-maven-plugin</artifactId>
<version>${tycho.version}</version>
<extensions>true</extensions>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>target-platform-configuration</artifactId>
<version>${tycho.version}</version>
<configuration>
<environments>
<environment>
<os>linux</os>
<ws>gtk</ws>
<arch>x86_64</arch>
</environment>
</environments>
</configuration>
</plugin>
</plugins>
</build>
</project>
Plugin POM
Each plugin project gets a minimal POM that inherits from the parent:
<?xml version="1.0" encoding="UTF-8"?>
<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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>com.example.parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.example.model</artifactId>
<packaging>eclipse-plugin</packaging>
</project>
Feature POM
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>com.example.parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.example.feature</artifactId>
<packaging>eclipse-feature</packaging>
</project>
Update Site POM
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>com.example.parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.example.p2</artifactId>
<packaging>eclipse-repository</packaging>
</project>
Building
Run the build from the root directory:
# Full build including tests
mvn clean verify
# Build without tests (faster)
mvn clean package -DskipTests
# Build and deploy to a local repository
mvn clean install
The P2 repository is generated in com.example.p2/target/repository/.
Continuous Integration Setup
Automating your build, test, and deployment pipeline ensures consistent quality and reduces manual errors.
GitHub Actions Workflow
Create a CI workflow at .github/workflows/build.yml:
name: Build and Test iDempiere Plugin
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_USER: adempiere
POSTGRES_PASSWORD: adempiere
POSTGRES_DB: idempiere
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'
- name: Build with Maven
run: mvn clean verify -B
- name: Upload P2 Repository
if: github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v4
with:
name: p2-repository
path: com.example.p2/target/repository/
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: '**/target/surefire-reports/*.xml'
Automated Testing in CI
Configure Tycho to run your JUnit tests during the build. For OSGi integration tests, use Tycho’s surefire plugin which launches a minimal OSGi runtime:
<!-- In the test plugin's pom.xml -->
<build>
<plugins>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-surefire-plugin</artifactId>
<version>${tycho.version}</version>
<configuration>
<testRuntime>p2Installed</testRuntime>
<argLine>-Xmx1g</argLine>
</configuration>
</plugin>
</plugins>
</build>
Versioning Strategy
Adopt semantic versioning for your plugins to communicate the nature of changes clearly:
Semantic Versioning (Major.Minor.Patch.Qualifier)
- Major (1.x.x): Breaking changes — removed or renamed APIs, incompatible database schema changes. Consumers of your plugin must update their code.
- Minor (x.1.x): New features — new endpoints, new model validators, additional fields. Backward-compatible with existing consumers.
- Patch (x.x.1): Bug fixes — corrected calculations, fixed null pointer exceptions, performance improvements. No API or behavior changes.
- Qualifier: Build metadata — typically a timestamp (e.g.,
1.2.3.202501150900) orSNAPSHOTfor development builds.
Version Management Across Projects
Keep versions synchronized across your plugin, feature, and update site. Use the Tycho versions plugin to update versions consistently:
# Update all project versions to 1.2.0-SNAPSHOT
mvn org.eclipse.tycho:tycho-versions-plugin:set-version -DnewVersion=1.2.0-SNAPSHOT
# Before release, set the release version
mvn org.eclipse.tycho:tycho-versions-plugin:set-version -DnewVersion=1.2.0
Version Ranges in Dependencies
When your feature declares dependencies on iDempiere core, use appropriate version ranges:
<!-- In feature.xml -->
<requires>
<!-- Compatible with any 11.x release -->
<import feature="org.adempiere.server" version="11.0.0" match="compatible"/>
</requires>
<!-- In MANIFEST.MF -->
Require-Bundle: org.adempiere.base;bundle-version="[11.0.0,12.0.0)"
The range [11.0.0,12.0.0) means “any version from 11.0.0 (inclusive) up to but not including 12.0.0.” This allows your plugin to work with any patch or minor update of iDempiere 11 without requiring rebuilds.
Deploying via P2
Once you have a P2 repository, deploy plugins to iDempiere servers using the P2 director.
Using the P2 Director
The P2 director is a command-line application for installing features from P2 repositories:
# Install your feature from a local P2 repository
/opt/idempiere/idempiere -nosplash -application org.eclipse.equinox.p2.director \
-repository file:///path/to/p2-repository/ \
-installIU com.example.feature.feature.group \
-destination /opt/idempiere \
-profile DefaultProfile
# Install from a remote HTTP repository
/opt/idempiere/idempiere -nosplash -application org.eclipse.equinox.p2.director \
-repository https://plugins.example.com/p2/ \
-installIU com.example.feature.feature.group \
-destination /opt/idempiere \
-profile DefaultProfile
# Uninstall a feature
/opt/idempiere/idempiere -nosplash -application org.eclipse.equinox.p2.director \
-uninstallIU com.example.feature.feature.group \
-destination /opt/idempiere \
-profile DefaultProfile
Hosting a P2 Repository
Serve your P2 repository via any HTTP server:
# Simple approach: host via nginx
server {
listen 443 ssl;
server_name plugins.example.com;
location /p2/ {
alias /var/www/p2-repository/;
autoindex on;
}
}
Upload the contents of com.example.p2/target/repository/ to the server, and the P2 director can install from the URL.
Docker-Based Deployment
Docker is increasingly popular for iDempiere deployments. Integrate your plugins into a custom Docker image:
Dockerfile for iDempiere with Custom Plugins
FROM idempiereofficial/idempiere:11
# Copy P2 repository into a temporary directory
COPY p2-repository/ /tmp/p2-repo/
# Install plugins using P2 director
RUN /opt/idempiere/idempiere -nosplash \
-application org.eclipse.equinox.p2.director \
-repository file:///tmp/p2-repo/ \
-installIU com.example.feature.feature.group \
-destination /opt/idempiere \
-profile DefaultProfile \
&& rm -rf /tmp/p2-repo/
# Copy any additional configuration
COPY custom-config/ /opt/idempiere/custom-config/
Docker Compose for Development
version: '3.8'
services:
db:
image: postgres:15
environment:
POSTGRES_USER: adempiere
POSTGRES_PASSWORD: adempiere
POSTGRES_DB: idempiere
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
idempiere:
build: .
depends_on:
- db
environment:
DB_HOST: db
DB_PORT: 5432
DB_NAME: idempiere
DB_USER: adempiere
DB_PASS: adempiere
ports:
- "8080:8080"
- "8443:8443"
- "4444:4444" # Debug port
volumes:
- idempiere-data:/opt/idempiere/data
volumes:
pgdata:
idempiere-data:
Release Management
A disciplined release process ensures reliable deployments.
Release Workflow
- Feature development: Work on feature branches, merged to
developvia pull requests with code review. - Release preparation: Create a release branch from
develop. Update version numbers (remove SNAPSHOT qualifier). Run full test suite. - Build release artifacts: Build the P2 repository from the release branch. Tag the commit with the version number.
- Deploy to staging: Install the release on a staging environment. Run integration tests and user acceptance testing.
- Deploy to production: Install the release on production using the P2 director. Verify functionality.
- Post-release: Merge the release branch to
mainand back todevelop. Bump version ondevelopto the next SNAPSHOT.
GitHub Actions Release Workflow
name: Release Plugin
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'
- name: Build release
run: mvn clean verify -B
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
files: |
com.example.p2/target/com.example.p2-*.zip
generate_release_notes: true
- name: Deploy P2 repository
run: |
# Upload P2 repo to your hosting server
rsync -avz com.example.p2/target/repository/ \
deploy@plugins.example.com:/var/www/p2-repository/
Dependency Management Between Plugins
When multiple teams or projects develop iDempiere plugins, managing inter-plugin dependencies becomes important.
Declaring Dependencies
Use Require-Bundle for compile-time dependencies on specific bundles, and Import-Package for loose coupling:
# Tight coupling: depends on a specific bundle
Require-Bundle: com.example.core;bundle-version="[1.0.0,2.0.0)"
# Loose coupling: depends on the package, not the bundle
Import-Package: com.example.core.api;version="[1.0.0,2.0.0)"
Import-Package is generally preferred because it allows the implementation bundle to change without affecting consumers. Use Require-Bundle only when you need access to multiple packages from a specific bundle or when using the bundle’s extension points.
API Stability Guidelines
- Export only API packages: In your
MANIFEST.MF, only export packages that form your public API. Keep implementation classes in non-exported packages. - Use interfaces: Define service interfaces in API packages and implementations in internal packages. Consumers depend on the interfaces, not the implementations.
- Document breaking changes: When you must make a breaking change, increment the major version and document migration steps in your changelog.
Practical Example: CI/CD Pipeline for an iDempiere Plugin
Here is a complete example tying everything together for a real-world workflow:
Repository Structure
my-idempiere-plugins/
com.example.model/ # Core model classes and validators
src/
META-INF/MANIFEST.MF
pom.xml
com.example.process/ # Custom processes
src/
META-INF/MANIFEST.MF
pom.xml
com.example.rest/ # REST API endpoints
src/
META-INF/MANIFEST.MF
pom.xml
com.example.model.test/ # Test bundle
src/
META-INF/MANIFEST.MF
pom.xml
com.example.feature/ # Feature project
feature.xml
pom.xml
com.example.p2/ # Update site
category.xml
pom.xml
.github/
workflows/
build.yml # CI workflow
release.yml # Release workflow
Dockerfile # Docker build
docker-compose.yml # Local dev environment
pom.xml # Parent POM
Developer Workflow
- Developer creates a feature branch:
git checkout -b feature/new-validation - Developer writes code and tests locally, running
mvn clean verify. - Developer pushes the branch and creates a pull request.
- GitHub Actions automatically builds and runs tests on the PR.
- Code is reviewed and merged to
develop. - When ready for release, a maintainer tags the commit:
git tag v1.2.0. - The release workflow builds the P2 repository and creates a GitHub release.
- The operations team deploys to staging using the P2 director, then promotes to production.
Summary
You have now learned the complete lifecycle of iDempiere plugin distribution — from organizing plugins into features and building P2 repositories with Maven/Tycho, through automated CI/CD pipelines with GitHub Actions, to production deployment via P2 director and Docker. Combined with semantic versioning, dependency management, and disciplined release processes, these practices enable you to deliver iDempiere customizations with the same rigor as any professional software product. This concludes the Advanced module of the iDempiere learning curriculum. You now have the knowledge to architect, develop, test, and deploy production-grade iDempiere plugins and integrations.
繁體中文翻譯
概述
- 學習目標:
- 了解 Eclipse P2 配置系統的運作方式,以及如何建立 Feature 專案和 Update Site 專案來發布 iDempiere 外掛
- 如何使用 Maven/Tycho 建置外掛、透過 GitHub Actions 設定持續整合,以及自動化測試與部署
- 如何管理外掛版本控制、相依性、基於 Docker 的部署,以及生產環境的發佈管理
- 先備知識:第 15 課——建置您的第一個外掛、第 33 課——測試與除錯外掛、對 Maven 和 Git 的基本熟悉度
- 預估閱讀時間:24 分鐘
導論
一個僅存在於開發者工作站上的外掛無法提供任何商業價值。要發揮作用,外掛必須經過打包、版本控制、自動化測試,並可靠地部署到生產伺服器。iDempiere 利用 Eclipse 的 P2 配置系統進行外掛發布——這與管理 Eclipse IDE 本身外掛的技術相同。結合 Maven/Tycho 進行建置和 CI/CD 管線進行自動化,您可以為 iDempiere 客製化建立專業的軟體交付流程。
本課涵蓋從原始碼到生產部署的完整流程:將外掛組織為 Feature、建置 P2 更新站台、使用持續整合自動化建置、管理版本和相依性,以及部署到包括 Docker 容器在內的生產環境。
P2 儲存庫概念
Eclipse P2(Provisioning Platform,配置平台)是一個用於安裝、更新和管理基於 Eclipse 的應用程式及其外掛的框架。iDempiere 建構於 Eclipse Equinox(一個 OSGi 實作)之上,這使得 P2 成為其原生的外掛發布機制。
P2 的運作方式
P2 儲存庫是一個結構化的目錄(本地或託管在網頁伺服器上),包含:
- Plugin JAR:包含您程式碼的已編譯 OSGi 套件。
- Feature JAR:將相關外掛組合為可安裝單元的中繼資料。
- content.xml / content.jar:儲存庫中所有可安裝單元(外掛和 Feature)的索引,包括其版本和相依性。
- artifacts.xml / artifacts.jar:實體成品檔案(JAR)及其下載位置的索引。
當您從 P2 儲存庫安裝外掛時,P2 引擎會解析所有相依性、下載所需的成品,並設定 OSGi 執行環境以包含新的套件。這確保外掛在安裝時所有先備條件都已滿足。
P2 vs. 手動 JAR 部署
雖然您可以透過手動複製 JAR 檔案到 plugins/ 目錄來部署外掛,但 P2 提供了關鍵優勢:
- 相依性解析:P2 確保安裝前所有所需的套件都已存在。
- 版本管理:P2 追蹤已安裝的版本,並能乾淨地升級、降級或解除安裝。
- 原子更新:P2 要麼成功安裝所有內容,要麼回復,防止部分安裝。
- 多重儲存庫:您可以設定多個 P2 儲存庫(iDempiere 核心 + 您的自訂外掛),P2 會跨所有儲存庫解析相依性。
Feature 專案
Feature 專案將一個或多個外掛組合為單一可安裝單元。可以將其視為「產品套件」——使用者安裝 Feature,所有包含的外掛就會一起部署。
建立 Feature 專案
- 在 Eclipse 中,選擇 File > New > Project > Plug-in Development > Feature Project。
- 按照慣例命名:
com.example.feature。 - 在「Referenced Plug-ins」頁面,選擇屬於此 Feature 的所有外掛。
feature.xml 檔案
feature.xml 檔案定義了 Feature 的中繼資料和內容:
<?xml version="1.0" encoding="UTF-8"?>
<feature
id="com.example.feature"
label="Example iDempiere Customization"
version="1.0.0.qualifier"
provider-name="Example Corp">
<description>
Custom business logic and reports for Example Corp's
iDempiere deployment.
</description>
<copyright>
Copyright (c) 2025 Example Corp. All rights reserved.
</copyright>
<license url="https://www.example.com/license">
[License text here]
</license>
<!-- Required iDempiere features (dependencies) -->
<requires>
<import feature="org.adempiere.server" version="11.0.0" match="greaterOrEqual"/>
<import feature="org.adempiere.ui.zk" version="11.0.0" match="greaterOrEqual"/>
</requires>
<!-- Plugins included in this feature -->
<plugin
id="com.example.model"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.example.process"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.example.reports"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.example.rest"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
</feature>
Feature 組織策略
對於大型客製化,考慮拆分為多個 Feature:
- com.example.core.feature:Model 類別、驗證器和所有其他元件使用的核心業務邏輯。
- com.example.ui.feature:UI 客製化、儀表板小工具和自訂表單(依賴於 core)。
- com.example.reports.feature:JasperReports 和報表流程(依賴於 core)。
- com.example.rest.feature:自訂 REST API 端點(依賴於 core)。
這種分離允許僅部署特定環境所需的元件(例如,REST Feature 可能只部署在 API 伺服器上,而不部署在面向使用者的伺服器上)。
Update Site 專案
Update Site 專案從您的 Feature 和外掛建置 P2 儲存庫。
建立 Update Site 專案
- 在 Eclipse 中,選擇 File > New > Project > Plug-in Development > Update Site Project。
- 命名為:
com.example.p2。 - 開啟
site.xml檔案並加入您的 Feature。
site.xml 檔案
<?xml version="1.0" encoding="UTF-8"?>
<site>
<feature url="features/com.example.feature_1.0.0.qualifier.jar"
id="com.example.feature"
version="1.0.0.qualifier">
<category name="example-customization"/>
</feature>
<category-def name="example-customization"
label="Example Corp Customization">
<description>Custom plugins for Example Corp deployment</description>
</category-def>
</site>
手動建置 Update Site
在 Eclipse 中,開啟 site.xml 編輯器並點選 Build All。Eclipse 會編譯所有外掛和 Feature,並在 Update Site 專案目錄中產生 P2 儲存庫成品。結果是如下的目錄結構:
com.example.p2/
features/
com.example.feature_1.0.0.202501150900.jar
plugins/
com.example.model_1.0.0.202501150900.jar
com.example.process_1.0.0.202501150900.jar
com.example.reports_1.0.0.202501150900.jar
com.example.rest_1.0.0.202501150900.jar
content.jar
artifacts.jar
使用 Maven/Tycho 建置
手動 Eclipse 建置適合開發,但自動化建置需要搭配 Tycho 外掛的 Maven。Tycho 教會 Maven 理解 Eclipse 外掛專案、Feature 專案和 P2 儲存庫。
父 POM
在專案儲存庫的根目錄建立父 POM:
<?xml version="1.0" encoding="UTF-8"?>
<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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>com.example.parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<tycho.version>4.0.4</tycho.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<idempiere.target>https://download.idempiere.org/11/p2/</idempiere.target>
</properties>
<modules>
<module>com.example.model</module>
<module>com.example.process</module>
<module>com.example.reports</module>
<module>com.example.rest</module>
<module>com.example.model.test</module>
<module>com.example.feature</module>
<module>com.example.p2</module>
</modules>
<repositories>
<repository>
<id>idempiere</id>
<url>${idempiere.target}</url>
<layout>p2</layout>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-maven-plugin</artifactId>
<version>${tycho.version}</version>
<extensions>true</extensions>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>target-platform-configuration</artifactId>
<version>${tycho.version}</version>
<configuration>
<environments>
<environment>
<os>linux</os>
<ws>gtk</ws>
<arch>x86_64</arch>
</environment>
</environments>
</configuration>
</plugin>
</plugins>
</build>
</project>
外掛 POM
每個外掛專案都有一個繼承自父 POM 的最小 POM:
<?xml version="1.0" encoding="UTF-8"?>
<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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>com.example.parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.example.model</artifactId>
<packaging>eclipse-plugin</packaging>
</project>
Feature POM
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>com.example.parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.example.feature</artifactId>
<packaging>eclipse-feature</packaging>
</project>
Update Site POM
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>com.example.parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.example.p2</artifactId>
<packaging>eclipse-repository</packaging>
</project>
建置
從根目錄執行建置:
# 完整建置(包含測試)
mvn clean verify
# 不含測試的建置(更快)
mvn clean package -DskipTests
# 建置並部署到本地儲存庫
mvn clean install
P2 儲存庫會產生在 com.example.p2/target/repository/。
持續整合設定
將建置、測試和部署管線自動化可確保一致的品質並減少人工錯誤。
GitHub Actions 工作流程
在 .github/workflows/build.yml 建立 CI 工作流程:
name: Build and Test iDempiere Plugin
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_USER: adempiere
POSTGRES_PASSWORD: adempiere
POSTGRES_DB: idempiere
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'
- name: Build with Maven
run: mvn clean verify -B
- name: Upload P2 Repository
if: github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v4
with:
name: p2-repository
path: com.example.p2/target/repository/
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: '**/target/surefire-reports/*.xml'
CI 中的自動化測試
設定 Tycho 在建置過程中執行 JUnit 測試。對於 OSGi 整合測試,使用 Tycho 的 surefire 外掛,它會啟動一個最小的 OSGi 執行環境:
<!-- In the test plugin's pom.xml -->
<build>
<plugins>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-surefire-plugin</artifactId>
<version>${tycho.version}</version>
<configuration>
<testRuntime>p2Installed</testRuntime>
<argLine>-Xmx1g</argLine>
</configuration>
</plugin>
</plugins>
</build>
版本控制策略
為您的外掛採用語意化版本控制,以清晰傳達變更的性質:
語意化版本控制(Major.Minor.Patch.Qualifier)
- Major(1.x.x):破壞性變更——移除或重新命名的 API、不相容的資料庫結構變更。您外掛的消費者必須更新其程式碼。
- Minor(x.1.x):新功能——新端點、新 Model 驗證器、額外的欄位。與現有消費者向後相容。
- Patch(x.x.1):錯誤修正——修正的計算、修復的空指標例外、效能改善。無 API 或行為變更。
- Qualifier:建置中繼資料——通常是時間戳(例如
1.2.3.202501150900)或開發版本的SNAPSHOT。
跨專案的版本管理
在外掛、Feature 和 Update Site 之間保持版本同步。使用 Tycho versions 外掛一致地更新版本:
# 將所有專案版本更新為 1.2.0-SNAPSHOT
mvn org.eclipse.tycho:tycho-versions-plugin:set-version -DnewVersion=1.2.0-SNAPSHOT
# 發佈前,設定發佈版本
mvn org.eclipse.tycho:tycho-versions-plugin:set-version -DnewVersion=1.2.0
相依性中的版本範圍
當您的 Feature 宣告對 iDempiere 核心的相依性時,使用適當的版本範圍:
<!-- In feature.xml -->
<requires>
<!-- Compatible with any 11.x release -->
<import feature="org.adempiere.server" version="11.0.0" match="compatible"/>
</requires>
<!-- In MANIFEST.MF -->
Require-Bundle: org.adempiere.base;bundle-version="[11.0.0,12.0.0)"
範圍 [11.0.0,12.0.0) 表示「從 11.0.0(含)到 12.0.0(不含)的任何版本」。這允許您的外掛與 iDempiere 11 的任何修補或次要更新搭配使用,無需重新建置。
透過 P2 部署
一旦您有了 P2 儲存庫,就可以使用 P2 director 將外掛部署到 iDempiere 伺服器。
使用 P2 Director
P2 director 是一個從 P2 儲存庫安裝 Feature 的命令列應用程式:
# 從本地 P2 儲存庫安裝您的 Feature
/opt/idempiere/idempiere -nosplash -application org.eclipse.equinox.p2.director \
-repository file:///path/to/p2-repository/ \
-installIU com.example.feature.feature.group \
-destination /opt/idempiere \
-profile DefaultProfile
# 從遠端 HTTP 儲存庫安裝
/opt/idempiere/idempiere -nosplash -application org.eclipse.equinox.p2.director \
-repository https://plugins.example.com/p2/ \
-installIU com.example.feature.feature.group \
-destination /opt/idempiere \
-profile DefaultProfile
# 解除安裝 Feature
/opt/idempiere/idempiere -nosplash -application org.eclipse.equinox.p2.director \
-uninstallIU com.example.feature.feature.group \
-destination /opt/idempiere \
-profile DefaultProfile
託管 P2 儲存庫
透過任何 HTTP 伺服器提供您的 P2 儲存庫:
# 簡單方式:透過 nginx 託管
server {
listen 443 ssl;
server_name plugins.example.com;
location /p2/ {
alias /var/www/p2-repository/;
autoindex on;
}
}
將 com.example.p2/target/repository/ 的內容上傳到伺服器,P2 director 就可以從該 URL 安裝。
基於 Docker 的部署
Docker 在 iDempiere 部署中越來越受歡迎。將您的外掛整合到自訂 Docker 映像中:
帶有自訂外掛的 iDempiere Dockerfile
FROM idempiereofficial/idempiere:11
# 將 P2 儲存庫複製到暫存目錄
COPY p2-repository/ /tmp/p2-repo/
# 使用 P2 director 安裝外掛
RUN /opt/idempiere/idempiere -nosplash \
-application org.eclipse.equinox.p2.director \
-repository file:///tmp/p2-repo/ \
-installIU com.example.feature.feature.group \
-destination /opt/idempiere \
-profile DefaultProfile \
&& rm -rf /tmp/p2-repo/
# 複製任何額外的設定
COPY custom-config/ /opt/idempiere/custom-config/
開發用 Docker Compose
version: '3.8'
services:
db:
image: postgres:15
environment:
POSTGRES_USER: adempiere
POSTGRES_PASSWORD: adempiere
POSTGRES_DB: idempiere
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
idempiere:
build: .
depends_on:
- db
environment:
DB_HOST: db
DB_PORT: 5432
DB_NAME: idempiere
DB_USER: adempiere
DB_PASS: adempiere
ports:
- "8080:8080"
- "8443:8443"
- "4444:4444" # Debug port
volumes:
- idempiere-data:/opt/idempiere/data
volumes:
pgdata:
idempiere-data:
發佈管理
有紀律的發佈流程確保可靠的部署。
發佈工作流程
- 功能開發:在功能分支上工作,透過附有程式碼審查的 Pull Request 合併到
develop。 - 發佈準備:從
develop建立發佈分支。更新版本號(移除 SNAPSHOT 限定詞)。執行完整測試套件。 - 建置發佈成品:從發佈分支建置 P2 儲存庫。以版本號標記提交。
- 部署到預備環境:在預備環境安裝發佈版本。執行整合測試和使用者驗收測試。
- 部署到生產環境:使用 P2 director 在生產環境安裝發佈版本。驗證功能。
- 發佈後:將發佈分支合併到
main並回合併到develop。在develop上將版本提升到下一個 SNAPSHOT。
GitHub Actions 發佈工作流程
name: Release Plugin
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'
- name: Build release
run: mvn clean verify -B
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
files: |
com.example.p2/target/com.example.p2-*.zip
generate_release_notes: true
- name: Deploy P2 repository
run: |
# Upload P2 repo to your hosting server
rsync -avz com.example.p2/target/repository/ \
deploy@plugins.example.com:/var/www/p2-repository/
外掛間的相依性管理
當多個團隊或專案開發 iDempiere 外掛時,管理外掛間的相依性變得很重要。
宣告相依性
使用 Require-Bundle 對特定套件的編譯時期相依性,使用 Import-Package 進行鬆散耦合:
# 緊密耦合:依賴特定的套件
Require-Bundle: com.example.core;bundle-version="[1.0.0,2.0.0)"
# 鬆散耦合:依賴套件(package),而非套件(bundle)
Import-Package: com.example.core.api;version="[1.0.0,2.0.0)"
Import-Package 通常較佳,因為它允許實作套件變更而不影響消費者。僅在需要存取特定套件的多個 package 或使用該套件的擴充點時才使用 Require-Bundle。
API 穩定性指南
- 僅匯出 API 套件:在
MANIFEST.MF中,僅匯出構成公開 API 的 package。將實作類別保留在未匯出的 package 中。 - 使用介面:在 API package 中定義服務介面,在內部 package 中定義實作。消費者依賴介面,而非實作。
- 記錄破壞性變更:當您必須進行破壞性變更時,遞增主要版本號並在變更日誌中記錄遷移步驟。
實作範例:iDempiere 外掛的 CI/CD 管線
以下是一個將所有內容整合在一起的完整範例,適用於真實世界的工作流程:
儲存庫結構
my-idempiere-plugins/
com.example.model/ # 核心 Model 類別和驗證器
src/
META-INF/MANIFEST.MF
pom.xml
com.example.process/ # 自訂流程
src/
META-INF/MANIFEST.MF
pom.xml
com.example.rest/ # REST API 端點
src/
META-INF/MANIFEST.MF
pom.xml
com.example.model.test/ # 測試套件
src/
META-INF/MANIFEST.MF
pom.xml
com.example.feature/ # Feature 專案
feature.xml
pom.xml
com.example.p2/ # Update Site
category.xml
pom.xml
.github/
workflows/
build.yml # CI 工作流程
release.yml # 發佈工作流程
Dockerfile # Docker 建置
docker-compose.yml # 本地開發環境
pom.xml # 父 POM
開發者工作流程
- 開發者建立功能分支:
git checkout -b feature/new-validation - 開發者在本地撰寫程式碼和測試,執行
mvn clean verify。 - 開發者推送分支並建立 Pull Request。
- GitHub Actions 自動在 PR 上建置並執行測試。
- 程式碼經過審查後合併到
develop。 - 準備發佈時,維護者標記提交:
git tag v1.2.0。 - 發佈工作流程建置 P2 儲存庫並建立 GitHub Release。
- 維運團隊使用 P2 director 部署到預備環境,然後升級到生產環境。
摘要
您現在已經學會了 iDempiere 外掛發布的完整生命週期——從將外掛組織為 Feature 並使用 Maven/Tycho 建置 P2 儲存庫,到透過 GitHub Actions 的自動化 CI/CD 管線,再到透過 P2 director 和 Docker 部署到生產環境。結合語意化版本控制、相依性管理和有紀律的發佈流程,這些實踐讓您能夠以與任何專業軟體產品相同的嚴謹度交付 iDempiere 客製化。本課為 iDempiere 學習課程的進階模組的結尾。您現在已擁有架構、開發、測試和部署生產級 iDempiere 外掛與整合的知識。
日本語翻訳
概要
- 学習内容:
- Eclipse P2 プロビジョニングの仕組みと、iDempiere プラグインを配布するための Feature プロジェクトおよび Update Site プロジェクトの作成方法
- Maven/Tycho によるプラグインのビルド、GitHub Actions による継続的インテグレーションの設定、テストとデプロイメントの自動化
- プラグインのバージョン管理、依存関係、Docker ベースのデプロイメント、および本番環境のリリース管理
- 前提条件:第15課——初めてのプラグイン構築、第33課——プラグインのテストとデバッグ、Maven と Git の基本的な知識
- 推定読了時間:24分
はじめに
開発者のワークステーションにしか存在しないプラグインは、ビジネス価値を提供しません。有用であるためには、プラグインはパッケージング、バージョン管理、自動テストを経て、本番サーバーに確実にデプロイされる必要があります。iDempiere は Eclipse の P2 プロビジョニングシステムをプラグイン配布に活用しています——これは Eclipse IDE 自体のプラグインを管理するのと同じ技術です。Maven/Tycho によるビルドと CI/CD パイプラインによる自動化を組み合わせることで、iDempiere カスタマイズのためのプロフェッショナルなソフトウェアデリバリープロセスを確立できます。
本レッスンでは、ソースコードから本番デプロイメントまでの完全な工程をカバーします:プラグインの Feature への組織化、P2 更新サイトの構築、継続的インテグレーションによるビルドの自動化、バージョンと依存関係の管理、そして Docker コンテナを含む本番環境へのデプロイメントです。
P2 リポジトリの概念
Eclipse P2(Provisioning Platform)は、Eclipse ベースのアプリケーションとそのプラグインをインストール、更新、管理するためのフレームワークです。iDempiere は Eclipse Equinox(OSGi 実装)上に構築されているため、P2 がネイティブなプラグイン配布メカニズムとなっています。
P2 の仕組み
P2 リポジトリは、構造化されたディレクトリ(ローカルまたは Web サーバー上でホスト)で、以下を含みます:
- Plugin JAR:コードを含むコンパイル済み OSGi バンドル。
- Feature JAR:関連するプラグインをインストール可能なユニットにグループ化するメタデータ。
- content.xml / content.jar:リポジトリ内のすべてのインストール可能ユニット(プラグインと Feature)のインデックス。バージョンと依存関係を含みます。
- artifacts.xml / artifacts.jar:物理的なアーティファクトファイル(JAR)とそのダウンロード場所のインデックス。
P2 リポジトリからプラグインをインストールすると、P2 エンジンはすべての依存関係を解決し、必要なアーティファクトをダウンロードし、新しいバンドルを含むように OSGi ランタイムを設定します。これにより、プラグインがすべての前提条件を満たした状態でインストールされることが保証されます。
P2 vs. 手動 JAR デプロイメント
JAR ファイルを plugins/ ディレクトリに手動でコピーしてプラグインをデプロイすることも可能ですが、P2 には重要な利点があります:
- 依存関係の解決:P2 はインストール前にすべての必要なバンドルが存在することを確認します。
- バージョン管理:P2 はインストール済みのバージョンを追跡し、クリーンなアップグレード、ダウングレード、アンインストールが可能です。
- アトミックな更新:P2 はすべてを正常にインストールするか、ロールバックするかのいずれかで、部分的なインストールを防ぎます。
- 複数リポジトリ:複数の P2 リポジトリ(iDempiere コア + カスタムプラグイン)を設定でき、P2 はすべてのリポジトリにわたって依存関係を解決します。
Feature プロジェクト
Feature プロジェクトは、1つ以上のプラグインを単一のインストール可能ユニットにグループ化します。「製品パッケージ」と考えてください——ユーザーが Feature をインストールすると、含まれるすべてのプラグインが一緒にデプロイされます。
Feature プロジェクトの作成
- Eclipse で、File > New > Project > Plug-in Development > Feature Project を選択します。
- 規則に従って命名します:
com.example.feature。 - 「Referenced Plug-ins」ページで、この Feature に属するすべてのプラグインを選択します。
feature.xml ファイル
feature.xml ファイルは Feature のメタデータと内容を定義します:
<?xml version="1.0" encoding="UTF-8"?>
<feature
id="com.example.feature"
label="Example iDempiere Customization"
version="1.0.0.qualifier"
provider-name="Example Corp">
<description>
Custom business logic and reports for Example Corp's
iDempiere deployment.
</description>
<copyright>
Copyright (c) 2025 Example Corp. All rights reserved.
</copyright>
<license url="https://www.example.com/license">
[License text here]
</license>
<!-- Required iDempiere features (dependencies) -->
<requires>
<import feature="org.adempiere.server" version="11.0.0" match="greaterOrEqual"/>
<import feature="org.adempiere.ui.zk" version="11.0.0" match="greaterOrEqual"/>
</requires>
<!-- Plugins included in this feature -->
<plugin
id="com.example.model"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.example.process"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.example.reports"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.example.rest"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
</feature>
Feature の組織戦略
大規模なカスタマイズでは、複数の Feature に分割することを検討してください:
- com.example.core.feature:Model クラス、バリデータ、およびすべての他のコンポーネントが使用するコアビジネスロジック。
- com.example.ui.feature:UI カスタマイズ、ダッシュボードガジェット、カスタムフォーム(core に依存)。
- com.example.reports.feature:JasperReports とレポートプロセス(core に依存)。
- com.example.rest.feature:カスタム REST API エンドポイント(core に依存)。
この分離により、特定の環境に必要なコンポーネントのみをデプロイできます(例えば、REST Feature は API サーバーにのみデプロイし、ユーザー向けサーバーにはデプロイしないなど)。
Update Site プロジェクト
Update Site プロジェクトは、Feature とプラグインから P2 リポジトリを構築します。
Update Site プロジェクトの作成
- Eclipse で、File > New > Project > Plug-in Development > Update Site Project を選択します。
- 命名します:
com.example.p2。 site.xmlファイルを開き、Feature を追加します。
site.xml ファイル
<?xml version="1.0" encoding="UTF-8"?>
<site>
<feature url="features/com.example.feature_1.0.0.qualifier.jar"
id="com.example.feature"
version="1.0.0.qualifier">
<category name="example-customization"/>
</feature>
<category-def name="example-customization"
label="Example Corp Customization">
<description>Custom plugins for Example Corp deployment</description>
</category-def>
</site>
Update Site の手動ビルド
Eclipse で site.xml エディタを開き、Build All をクリックします。Eclipse はすべてのプラグインと Feature をコンパイルし、Update Site プロジェクトディレクトリに P2 リポジトリのアーティファクトを生成します。結果は以下のようなディレクトリ構造になります:
com.example.p2/
features/
com.example.feature_1.0.0.202501150900.jar
plugins/
com.example.model_1.0.0.202501150900.jar
com.example.process_1.0.0.202501150900.jar
com.example.reports_1.0.0.202501150900.jar
com.example.rest_1.0.0.202501150900.jar
content.jar
artifacts.jar
Maven/Tycho によるビルド
手動の Eclipse ビルドは開発には適していますが、自動化ビルドには Tycho プラグインを使用した Maven が必要です。Tycho は Maven に Eclipse プラグインプロジェクト、Feature プロジェクト、P2 リポジトリを理解させます。
親 POM
プロジェクトリポジトリのルートに親 POM を作成します:
<?xml version="1.0" encoding="UTF-8"?>
<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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>com.example.parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<tycho.version>4.0.4</tycho.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<idempiere.target>https://download.idempiere.org/11/p2/</idempiere.target>
</properties>
<modules>
<module>com.example.model</module>
<module>com.example.process</module>
<module>com.example.reports</module>
<module>com.example.rest</module>
<module>com.example.model.test</module>
<module>com.example.feature</module>
<module>com.example.p2</module>
</modules>
<repositories>
<repository>
<id>idempiere</id>
<url>${idempiere.target}</url>
<layout>p2</layout>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-maven-plugin</artifactId>
<version>${tycho.version}</version>
<extensions>true</extensions>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>target-platform-configuration</artifactId>
<version>${tycho.version}</version>
<configuration>
<environments>
<environment>
<os>linux</os>
<ws>gtk</ws>
<arch>x86_64</arch>
</environment>
</environments>
</configuration>
</plugin>
</plugins>
</build>
</project>
プラグイン POM
各プラグインプロジェクトには、親 POM を継承する最小限の POM があります:
<?xml version="1.0" encoding="UTF-8"?>
<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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>com.example.parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.example.model</artifactId>
<packaging>eclipse-plugin</packaging>
</project>
Feature POM
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>com.example.parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.example.feature</artifactId>
<packaging>eclipse-feature</packaging>
</project>
Update Site POM
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>com.example.parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.example.p2</artifactId>
<packaging>eclipse-repository</packaging>
</project>
ビルド
ルートディレクトリからビルドを実行します:
# テストを含むフルビルド
mvn clean verify
# テストなしのビルド(高速)
mvn clean package -DskipTests
# ビルドしてローカルリポジトリにデプロイ
mvn clean install
P2 リポジトリは com.example.p2/target/repository/ に生成されます。
継続的インテグレーションの設定
ビルド、テスト、デプロイメントのパイプラインを自動化することで、一貫した品質が確保され、手動エラーが削減されます。
GitHub Actions ワークフロー
.github/workflows/build.yml に CI ワークフローを作成します:
name: Build and Test iDempiere Plugin
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_USER: adempiere
POSTGRES_PASSWORD: adempiere
POSTGRES_DB: idempiere
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'
- name: Build with Maven
run: mvn clean verify -B
- name: Upload P2 Repository
if: github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v4
with:
name: p2-repository
path: com.example.p2/target/repository/
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: '**/target/surefire-reports/*.xml'
CI での自動テスト
ビルドプロセス中に JUnit テストを実行するように Tycho を設定します。OSGi 統合テストには、最小限の OSGi ランタイムを起動する Tycho の surefire プラグインを使用します:
<!-- In the test plugin's pom.xml -->
<build>
<plugins>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-surefire-plugin</artifactId>
<version>${tycho.version}</version>
<configuration>
<testRuntime>p2Installed</testRuntime>
<argLine>-Xmx1g</argLine>
</configuration>
</plugin>
</plugins>
</build>
バージョニング戦略
変更の性質を明確に伝えるために、プラグインにセマンティックバージョニングを採用します:
セマンティックバージョニング(Major.Minor.Patch.Qualifier)
- Major(1.x.x):破壊的変更——削除または名前変更された API、互換性のないデータベーススキーマ変更。プラグインの利用者はコードを更新する必要があります。
- Minor(x.1.x):新機能——新しいエンドポイント、新しい Model バリデータ、追加フィールド。既存の利用者と後方互換性があります。
- Patch(x.x.1):バグ修正——計算の修正、null ポインタ例外の修正、パフォーマンスの改善。API や動作の変更はありません。
- Qualifier:ビルドメタデータ——通常はタイムスタンプ(例:
1.2.3.202501150900)または開発ビルドのSNAPSHOT。
プロジェクト間のバージョン管理
プラグイン、Feature、Update Site 間でバージョンを同期させます。Tycho versions プラグインを使用してバージョンを一貫して更新します:
# すべてのプロジェクトバージョンを 1.2.0-SNAPSHOT に更新
mvn org.eclipse.tycho:tycho-versions-plugin:set-version -DnewVersion=1.2.0-SNAPSHOT
# リリース前にリリースバージョンを設定
mvn org.eclipse.tycho:tycho-versions-plugin:set-version -DnewVersion=1.2.0
依存関係のバージョン範囲
Feature が iDempiere コアへの依存関係を宣言する場合、適切なバージョン範囲を使用します:
<!-- In feature.xml -->
<requires>
<!-- Compatible with any 11.x release -->
<import feature="org.adempiere.server" version="11.0.0" match="compatible"/>
</requires>
<!-- In MANIFEST.MF -->
Require-Bundle: org.adempiere.base;bundle-version="[11.0.0,12.0.0)"
範囲 [11.0.0,12.0.0) は「11.0.0(含む)から 12.0.0(含まない)までの任意のバージョン」を意味します。これにより、プラグインは iDempiere 11 の任意のパッチまたはマイナーアップデートで動作し、リビルドは不要です。
P2 によるデプロイ
P2 リポジトリが準備できたら、P2 director を使用して iDempiere サーバーにプラグインをデプロイします。
P2 Director の使用
P2 director は P2 リポジトリから Feature をインストールするコマンドラインアプリケーションです:
# ローカル P2 リポジトリから Feature をインストール
/opt/idempiere/idempiere -nosplash -application org.eclipse.equinox.p2.director \
-repository file:///path/to/p2-repository/ \
-installIU com.example.feature.feature.group \
-destination /opt/idempiere \
-profile DefaultProfile
# リモート HTTP リポジトリからインストール
/opt/idempiere/idempiere -nosplash -application org.eclipse.equinox.p2.director \
-repository https://plugins.example.com/p2/ \
-installIU com.example.feature.feature.group \
-destination /opt/idempiere \
-profile DefaultProfile
# Feature のアンインストール
/opt/idempiere/idempiere -nosplash -application org.eclipse.equinox.p2.director \
-uninstallIU com.example.feature.feature.group \
-destination /opt/idempiere \
-profile DefaultProfile
P2 リポジトリのホスティング
任意の HTTP サーバーで P2 リポジトリを提供します:
# シンプルなアプローチ:nginx でホスト
server {
listen 443 ssl;
server_name plugins.example.com;
location /p2/ {
alias /var/www/p2-repository/;
autoindex on;
}
}
com.example.p2/target/repository/ の内容をサーバーにアップロードすれば、P2 director はその URL からインストールできます。
Docker ベースのデプロイメント
Docker は iDempiere のデプロイメントでますます人気が高まっています。カスタム Docker イメージにプラグインを統合します:
カスタムプラグイン付き iDempiere の Dockerfile
FROM idempiereofficial/idempiere:11
# P2 リポジトリを一時ディレクトリにコピー
COPY p2-repository/ /tmp/p2-repo/
# P2 director を使用してプラグインをインストール
RUN /opt/idempiere/idempiere -nosplash \
-application org.eclipse.equinox.p2.director \
-repository file:///tmp/p2-repo/ \
-installIU com.example.feature.feature.group \
-destination /opt/idempiere \
-profile DefaultProfile \
&& rm -rf /tmp/p2-repo/
# 追加の設定をコピー
COPY custom-config/ /opt/idempiere/custom-config/
開発用 Docker Compose
version: '3.8'
services:
db:
image: postgres:15
environment:
POSTGRES_USER: adempiere
POSTGRES_PASSWORD: adempiere
POSTGRES_DB: idempiere
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
idempiere:
build: .
depends_on:
- db
environment:
DB_HOST: db
DB_PORT: 5432
DB_NAME: idempiere
DB_USER: adempiere
DB_PASS: adempiere
ports:
- "8080:8080"
- "8443:8443"
- "4444:4444" # デバッグポート
volumes:
- idempiere-data:/opt/idempiere/data
volumes:
pgdata:
idempiere-data:
リリース管理
規律のあるリリースプロセスにより、信頼性の高いデプロイメントが保証されます。
リリースワークフロー
- 機能開発:フィーチャーブランチで作業し、コードレビュー付きの Pull Request で
developにマージします。 - リリース準備:
developからリリースブランチを作成します。バージョン番号を更新します(SNAPSHOT 修飾子を削除)。完全なテストスイートを実行します。 - リリースアーティファクトのビルド:リリースブランチから P2 リポジトリをビルドします。バージョン番号でコミットにタグを付けます。
- ステージング環境へのデプロイ:ステージング環境にリリースをインストールします。統合テストとユーザー受け入れテストを実行します。
- 本番環境へのデプロイ:P2 director を使用して本番環境にリリースをインストールします。機能を検証します。
- リリース後:リリースブランチを
mainにマージし、developにもマージバックします。developで次の SNAPSHOT にバージョンを上げます。
GitHub Actions リリースワークフロー
name: Release Plugin
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'
- name: Build release
run: mvn clean verify -B
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
files: |
com.example.p2/target/com.example.p2-*.zip
generate_release_notes: true
- name: Deploy P2 repository
run: |
# Upload P2 repo to your hosting server
rsync -avz com.example.p2/target/repository/ \
deploy@plugins.example.com:/var/www/p2-repository/
プラグイン間の依存関係管理
複数のチームまたはプロジェクトが iDempiere プラグインを開発する場合、プラグイン間の依存関係の管理が重要になります。
依存関係の宣言
特定のバンドルへのコンパイル時依存関係には Require-Bundle を、疎結合には Import-Package を使用します:
# 密結合:特定のバンドルに依存
Require-Bundle: com.example.core;bundle-version="[1.0.0,2.0.0)"
# 疎結合:バンドルではなくパッケージに依存
Import-Package: com.example.core.api;version="[1.0.0,2.0.0)"
Import-Package は一般的に推奨されます。実装バンドルがコンシューマーに影響を与えずに変更できるためです。特定のバンドルの複数のパッケージにアクセスする必要がある場合や、バンドルの拡張ポイントを使用する場合にのみ Require-Bundle を使用してください。
API 安定性ガイドライン
- API パッケージのみをエクスポート:
MANIFEST.MFで、パブリック API を構成するパッケージのみをエクスポートします。実装クラスはエクスポートされないパッケージに保持します。 - インターフェースの使用:API パッケージでサービスインターフェースを定義し、内部パッケージで実装を定義します。コンシューマーは実装ではなくインターフェースに依存します。
- 破壊的変更の文書化:破壊的変更を行う必要がある場合、メジャーバージョンをインクリメントし、変更ログに移行手順を文書化します。
実践例:iDempiere プラグインの CI/CD パイプライン
以下は、実際のワークフローのためにすべてを統合した完全な例です:
リポジトリ構造
my-idempiere-plugins/
com.example.model/ # コア Model クラスとバリデータ
src/
META-INF/MANIFEST.MF
pom.xml
com.example.process/ # カスタムプロセス
src/
META-INF/MANIFEST.MF
pom.xml
com.example.rest/ # REST API エンドポイント
src/
META-INF/MANIFEST.MF
pom.xml
com.example.model.test/ # テストバンドル
src/
META-INF/MANIFEST.MF
pom.xml
com.example.feature/ # Feature プロジェクト
feature.xml
pom.xml
com.example.p2/ # Update Site
category.xml
pom.xml
.github/
workflows/
build.yml # CI ワークフロー
release.yml # リリースワークフロー
Dockerfile # Docker ビルド
docker-compose.yml # ローカル開発環境
pom.xml # 親 POM
開発者ワークフロー
- 開発者がフィーチャーブランチを作成:
git checkout -b feature/new-validation - 開発者がローカルでコードとテストを記述し、
mvn clean verifyを実行します。 - 開発者がブランチをプッシュし、Pull Request を作成します。
- GitHub Actions が PR 上で自動的にビルドとテストを実行します。
- コードがレビューされ、
developにマージされます。 - リリースの準備ができたら、メンテナーがコミットにタグを付けます:
git tag v1.2.0。 - リリースワークフローが P2 リポジトリをビルドし、GitHub Release を作成します。
- 運用チームが P2 director を使用してステージングにデプロイし、その後本番環境に昇格させます。
まとめ
iDempiere プラグイン配布の完全なライフサイクルを学びました——プラグインの Feature への組織化と Maven/Tycho による P2 リポジトリの構築から、GitHub Actions による自動化された CI/CD パイプライン、そして P2 director と Docker による本番環境へのデプロイメントまで。セマンティックバージョニング、依存関係管理、規律のあるリリースプロセスを組み合わせることで、これらのプラクティスにより、プロフェッショナルなソフトウェア製品と同じ厳格さで iDempiere カスタマイズを提供できます。本レッスンで iDempiere 学習カリキュラムのアドバンストモジュールは完了です。本番グレードの iDempiere プラグインとインテグレーションを設計、開発、テスト、デプロイするための知識を習得しました。