Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,5 @@
/tools/sf-dependency-list @yuriylesyuk
/tools/target-server-validator @anaik91
/tools/apigee-proxy-modifier-validator @anaik91
/tools/apigee-edge-to-x-migration-tool @h-kopalle
/tools/apigee-edge-to-x-migration-tool @h-kopalle
/tools/apigee-config-diff @rickygodoy
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ Apigee products.
- [Apigee Proxy Bundle Modifier & Validator](tools/apigee-proxy-modifier-validator) -
A tool to do batch modifications of API proxies when migrating to newer Apigee variants.
- [Apigee Edge to X Migration Accelerator](/tools/apigee-edge-to-x-migration-tool) - A tool built on node.js to accelerate Apigee Edge to X migration.
- [Apigee Config Diff Tool](tools/apigee-config-diff) -
A tool to find and deploy only the changes in Apigee configurations.

## Labs

Expand Down
12 changes: 12 additions & 0 deletions tools/apigee-config-diff/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.DS_Store
.idea
*.log
tmp/

*.py[cod]
*.egg
build
htmlcov
venv
output
__pycache__
115 changes: 115 additions & 0 deletions tools/apigee-config-diff/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<p align="center">
<img src="logo.svg" alt="Apigee Config Diff Generator Logo" width="500">
</p>

# Apigee Config Diff Tool

**Deploy only what changed in your Apigee configurations.**

This tool finds differences in Apigee Configuration files (KVMs, Target Servers, API Products, etc.) between two Git commits and uses the [Apigee Config Maven Plugin](https://github.com/apigee/apigee-config-maven-plugin) to apply only those changes.

## Why use this?

Standard Apigee configuration deployments often re-deploy the entire configuration set, which can be slow and risky for large organizations. This tool allows for **incremental deployments**:
- **Speed:** Only process files and resources that actually changed.
- **Safety:** Reduce the risk of accidental overwrites of stable configurations.
- **CI/CD Friendly:** Designed to run in pipelines (Jenkins, GitHub Actions, GitLab, etc.).

---

## How it Works

1. **Diff:** Compares two Git commits (e.g., `HEAD~1` vs `HEAD`).
2. **Generate:** Creates two temporary trees in `output/`:
- `update/`: New or modified resources.
- `delete/`: Resources removed or items deleted from within modified files.
3. **Deploy:** Runs `mvn install` on both trees with the `update` or `delete` option.

---

## Quick Start

### 1. Requirements
- Python 3.10+
- Maven (`mvn`) installed and in your PATH.
- (Optional) `tree` command for better dry-run visualization.

### 2. Installation
Install directly from GitHub:
```bash
pip install git+https://github.com/apigee/devrel.git#subdirectory=tools/apigee-config-diff
```

### 3. Basic Usage (Dry Run)
By default, the tool compares `HEAD~1` with `HEAD` in the `resources/` directory.
```bash
apigee-config-diff
```

### 4. Deploy Changes
Add the `--confirm` flag to actually execute the Maven commands.
```bash
apigee-config-diff --confirm
```

---

## Configuration & Arguments

| Argument | Default | Description |
| :--- | :--- | :--- |
| `--commit-before` | `HEAD~1` | Previous commit hash to compare. |
| `--current-commit` | `HEAD` | Current commit hash. |
| `--folder` | `resources` | Folder containing the Apigee config tree. |
| `--output` | `output` | Where to generate the temporary diff trees. |
| `--confirm` | `False` | Must be present to execute `mvn` commands. |
| `--bearer` | `None` | Optional: Apigee bearer token to use for Maven. |
| `--sa-path` | `None` | Optional: Path to the service account key file. |

### Authentication
The tool passes authentication flags directly to the Maven command. You can provide them in three ways:

1. **Command Line (Recommended):**
```bash
apigee-config-diff --confirm --bearer "$(gcloud auth print-access-token)"
# OR
apigee-config-diff --confirm --sa-path /tmp/sa.json
```

2. **Environment Variables:**
If your `pom.xml` is configured to use environment variables (e.g., `${env.bearer}`), simply export them before running the script:
```bash
export bearer=$(gcloud auth print-access-token)
apigee-config-diff --confirm
```

3. **POM Configuration:**
Hardcode the service account path or token directly in your `pom.xml` (not recommended for CI/CD).

---

## Expected Folder Structure
The tool expects the standard [Maven config structure](https://github.com/apigee/apigee-config-maven-plugin?tab=readme-ov-file#multi-file-config) inside your `--folder` (default `resources/`):

```text
<org-name>/
├── org/
│ ├── apiProducts.json
│ ├── developers.json
│ └── ...
└── env/
├── <env-name>/
│ ├── targetServers.json
│ ├── kvms.json
│ └── ...
```

***Note**: File names must start with the resource type (e.g., `targetServers-backend.json` is valid).*

---

## Contributing
Run tests with:
```bash
pytest
```
200 changes: 200 additions & 0 deletions tools/apigee-config-diff/example/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
<?xml version="1.0"?>
<!--
Copyright 2026 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<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>apigee</groupId>
<artifactId>parent-pom</artifactId>
<packaging>pom</packaging>
<version>1.0</version>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Maven Plugin Repository</name>
<url>https://repo1.maven.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>
<!-- Since Maven 3.0.3, for two plugins bound to the same phase, the order
of execution is the same as the order in which you define them. -->
<build>
<plugins>
<plugin>
<groupId>com.apigee.edge.config</groupId>
<artifactId>apigee-config-maven-plugin</artifactId>
<version>2.9.3</version>
<executions>
<execution>
<id>create-config-targetserver</id>
<phase>install</phase>
<goals>
<goal>targetservers</goal>
</goals>
</execution>
<execution>
<id>create-config-kvm</id>
<phase>install</phase>
<goals>
<goal>keyvaluemaps</goal>
</goals>
</execution>
<execution>
<id>create-config-keystores</id>
<phase>install</phase>
<goals>
<goal>keystores</goal>
</goals>
</execution>
<execution>
<id>create-config-aliases</id>
<phase>install</phase>
<goals>
<goal>aliases</goal>
</goals>
</execution>
<execution>
<id>create-config-references</id>
<phase>install</phase>
<goals>
<goal>references</goal>
</goals>
</execution>
<execution>
<id>create-config-resourcefiles</id>
<phase>install</phase>
<goals>
<goal>resourcefiles</goal>
</goals>
</execution>
<execution>
<id>create-config-apiproduct</id>
<phase>install</phase>
<goals>
<goal>apiproducts</goal>
</goals>
</execution>
<execution>
<id>create-config-rateplans</id>
<phase>install</phase>
<goals>
<goal>rateplans</goal>
</goals>
</execution>
<execution>
<id>create-config-developer</id>
<phase>install</phase>
<goals>
<goal>developers</goal>
</goals>
</execution>
<execution>
<id>create-config-app</id>
<phase>install</phase>
<goals>
<goal>apps</goal>
</goals>
</execution>
<execution>
<id>create-config-appgroups</id>
<phase>install</phase>
<goals>
<goal>appgroups</goal>
</goals>
</execution>
<execution>
<id>create-config-appgroupapps</id>
<phase>install</phase>
<goals>
<goal>appgroupapps</goal>
</goals>
</execution>
<execution>
<id>create-config-reports</id>
<phase>install</phase>
<goals>
<goal>reports</goal>
</goals>
</execution>
<execution>
<id>create-config-flowhooks</id>
<phase>install</phase>
<goals>
<goal>flowhooks</goal>
</goals>
</execution>
<execution>
<id>import-app-keys</id>
<phase>install</phase>
<goals>
<goal>importKeys</goal>
</goals>
</execution>
<execution>
<id>create-portal-categories</id>
<phase>install</phase>
<goals>
<goal>apicategories</goal>
</goals>
</execution>
<execution>
<id>create-portal-docs</id>
<phase>install</phase>
<goals>
<goal>apidocs</goal>
</goals>
</execution>
<execution>
<id>create-spaces</id>
<phase>install</phase>
<goals>
<goal>spaces</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>my-org</id>
<properties>
<apigee.profile>my-org</apigee.profile>
<apigee.hosturl>https://apigee.googleapis.com</apigee.hosturl>
<apigee.apiversion>v1</apigee.apiversion>
<apigee.org>${org}</apigee.org>
<apigee.env>${env}</apigee.env>
<apigee.authtype>oauth</apigee.authtype>
<apigee.bearer>${bearer}</apigee.bearer>
<apigee.portal.siteId>${siteId}</apigee.portal.siteId>
<apigee.serviceaccount.file>${file}</apigee.serviceaccount.file>
</properties>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"alias": "my-alias",
"keystorename": "my-keystore",
"format": "selfsignedcert",
"keySize": "2048",
"sigAlg": "SHA256withRSA",
"subject": {
"commonName": "test.example.com"
},
"certValidityInDays": "365"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"name": "my-cache",
"description": "General purpose cache",
"expirySettings": {
"values": {
"expiryDate": {
"value": "12-31-2025"
}
}
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"flowHookPoint": "PreProxyFlowHook",
"sharedFlow": "security-shared-flow"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
{
"name": "my-keystore"
}
]
Loading