DEV Community

Query Filter
Query Filter

Posted on

docker37

Got it — let’s restart clean and design this properly, without overengineering.

This is a simple, robust hybrid model that is easy to explain, implement, and scale.


🚀 Hybrid RPM Deployment Model (Clean Design)

🎯 Core Idea

We use BUILD_NUMBER as the only shared identifier across all systems.

Everything else is derived from it.


🔄 End-to-End Flow

Tekton (Build Stage)
   │
   ├── builds RPM
   ├── generates manifest.json
   ├── uploads BOTH to Artifactory under BUILD_NUMBER
   └── outputs BUILD_NUMBER
        │
        ▼
Harness (Orchestration)
   │
   ├── receives BUILD_NUMBER
   ├── downloads manifest.json from Artifactory
   ├── extracts rpm_name
   └── passes rpm_name to Ansible
        │
        ▼
Ansible (Deployment)
   │
   ├── uses rpm_name
   └── downloads RPM from Artifactory
   └── installs / upgrades
Enter fullscreen mode Exit fullscreen mode

📦 Artifactory Layout (IMPORTANT)

Each build is isolated:

/artifactory/repo/my-app/
    └── 111-7cf39ab-leonid-comet/
        ├── manifest.json
        └── my-app-1.2.3.rpm
Enter fullscreen mode Exit fullscreen mode

👉 Key rule: BUILD_NUMBER is the folder key


🧾 Manifest Contract

Example manifest.json

{
  "build_number": "111-7cf39ab-leonid-comet",
  "rpm_name": "my-app-1.2.3.rpm",
  "branch": "leonid-comet",
  "git_commit": "7cf39ab"
}
Enter fullscreen mode Exit fullscreen mode

🏗️ Tekton + Gradle Stage

1. Build RPM

./gradlew buildRpm
Enter fullscreen mode Exit fullscreen mode

2. Generate manifest (Gradle task)

task generateManifest {
    doLast {
        def buildNumber = System.getenv("BUILD_NUMBER")
        def rpmName = "${project.name}-${version}.rpm"

        def manifest = [
            build_number: buildNumber,
            rpm_name: rpmName,
            branch: buildNumber.split('-').last(),
            git_commit: buildNumber.split('-')[1]
        ]

        file("${buildDir}/manifest.json").text =
            groovy.json.JsonOutput.prettyPrint(
                groovy.json.JsonOutput.toJson(manifest)
            )
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Upload to Artifactory

curl -u user:pass -T build/my-app-1.2.3.rpm \
  "https://artifactory/repo/my-app/${BUILD_NUMBER}/"

curl -u user:pass -T build/manifest.json \
  "https://artifactory/repo/my-app/${BUILD_NUMBER}/"
Enter fullscreen mode Exit fullscreen mode

4. Output build number (Tekton result)

echo "$BUILD_NUMBER" > $(results.build_number.path)
Enter fullscreen mode Exit fullscreen mode

⚙️ Harness Stage

1. Receive build number

- set_fact:
    build_number: "{{ lookup('env', 'BUILD_NUMBER') }}"
Enter fullscreen mode Exit fullscreen mode

2. Download manifest

- name: Fetch manifest
  get_url:
    url: "https://artifactory/repo/my-app/{{ build_number }}/manifest.json"
    dest: /tmp/manifest.json
Enter fullscreen mode Exit fullscreen mode

3. Parse manifest

- set_fact:
    manifest: "{{ lookup('file', '/tmp/manifest.json') | from_json }}"
Enter fullscreen mode Exit fullscreen mode

4. Extract RPM name

- set_fact:
    rpm_name: "{{ manifest.rpm_name }}"
Enter fullscreen mode Exit fullscreen mode

🧰 Ansible Stage

1. Download RPM

- name: Download RPM
  get_url:
    url: "https://artifactory/repo/my-app/{{ build_number }}/{{ rpm_name }}"
    dest: "/tmp/{{ rpm_name }}"
Enter fullscreen mode Exit fullscreen mode

2. Install RPM

- name: Install package
  command: rpm -Uvh /tmp/{{ rpm_name }}
Enter fullscreen mode Exit fullscreen mode

🧠 Why this design works

✅ 1. Fully deterministic

Everything is tied to:

BUILD_NUMBER
Enter fullscreen mode Exit fullscreen mode

No guessing. No regex. No “latest”.


✅ 2. Safe for parallel builds

Each run is isolated:

111-xxx/
112-yyy/
113-zzz/
Enter fullscreen mode Exit fullscreen mode

No collisions possible.


✅ 3. Clean separation of responsibilities

System Responsibility
Tekton Build + publish artifacts
Artifactory Source of truth
Harness orchestration
Ansible deployment only

✅ 4. Easy rollback

Just redeploy:

BUILD_NUMBER = previous value
Enter fullscreen mode Exit fullscreen mode

✅ 5. No tight coupling between tools

Ansible never knows:

  • how RPM is named
  • how build was created
  • how Gradle works

🚫 What this avoids

❌ hardcoded filenames
❌ wildcard searching in Artifactory
❌ shared “latest” directories
❌ pipeline-dependent variables everywhere


🎯 Final architecture statement (for team discussion)

We standardize on BUILD_NUMBER as the single global artifact key.
Each build publishes a self-contained manifest and RPM into Artifactory under this key.
Deployment stages resolve artifacts strictly via the manifest, ensuring deterministic, concurrent-safe, and fully reproducible deployments.


Top comments (0)