Artifactory Project — Description and Analysis

1. Executive summary

The supplied files describe a partially developed Perl/Moo-based toolset for managing DAP application binaries stored in Artifactory and making them available on destination hosts.

At a high level, the project appears intended to:

1. Validate that a named release exists in Artifactory. 2. Read release metadata from Artifactory. 3. Update a local status.xml manifest describing supported products and JDKs. 4. Download product archives such as WebLogic, Tomcat, JBoss and JDK tarballs from Artifactory. 5. Stage or extract those archives onto destination hosts. 6. Optionally process local .dap metadata files to update status.xml.

The main implemented workflows are split between:

- artifactory-release, which is a CLI script for checking a release and updating status.xml. - lib/Dap/Artifactory/Manifest.pm, which reads remote release metadata and rewrites sections of status.xml. - lib/Dap/Artifactory.pm, which downloads and stages/extracts products and JDKs from Artifactory. - lib/Dap/Artifactory/Tx.pm, which provides the Artifactory HTTP/curl transaction layer. - lib/Dap/Artifactory/Dapfile*.pm, which seem intended to process per-package .dap JSON metadata files, although this area is incomplete.

There are several implementation bugs and inconsistencies, so the project should be regarded as incomplete or not yet production-safe.

According to artifactory-release, the first visible use case is:

artifactory-release --release <release-path> --statusxml <status.xml>

has status_xml => ( is => 'rw', required => 1 );
has release => ( is => 'rw', required => 1 );

if (not $self->tx->file_exist($self->release)) {
$self->log->error("RELEASE: " . $self->release . ' NOT FOUND on Artifactory');
exit 1;
}

$self->log->info('RELEASE ' . $self->release . ' OK');

Dap::Artifactory::Manifest->new(
release => $self->release,
status_xml => $self->status_xml,
)->update;

The release metadata itself is expected to live in Artifactory under a path based on:

/dist-private-local/com/db/dap/<release>

as implemented in lib/Dap/Artifactory/Manifest.pm and lib/Dap/Artifactory/Tx.pm.

The second use case, represented by lib/Dap/Artifactory.pm, is installation/staging:

1. Read a manifest. 2. For each product/JDK in the manifest: - Check if it is already installed or staged. - If not, verify the archive exists in Artifactory. - Download it. - For WebLogic, move the archive into a WebLogic staging tarball directory. - For Tomcat/JBoss/JDK, extract the archive into the staging application directory.

The third partial use case, represented by lib/Dap/Artifactory/Dapfile.pm, is discovery of .dap metadata files inside package directories and conversion of that metadata into status.xml entries.

3.1 CLI release updater

File: `artifactory-release`

artifactory-release defines package Dap::Artifactory::Release and a main block.

It accepts two command-line options through Dap::Script:

"release|r=s"  => \my $release,
"statusxml=s"  => \my $status_xml,

The object requires:

has status_xml => ( is => 'rw', required => 1 );
has release    => ( is => 'rw', required => 1 );

Its run method does:

if (not $self->tx->file_exist($self->release)) {
    $self->log->error("RELEASE: " . $self->release . ' NOT FOUND on Artifactory');
    exit 1;
}

$self->log->info('RELEASE ' . $self->release . ' OK');

Dap::Artifactory::Manifest->new(
    release    => $self->release,
    status_xml => $self->status_xml,
)->update;

So the intended behavior is:

1. Confirm the release path exists in Artifactory. 2. Update the provided status.xml using release metadata.

Inputs

From artifactory-release:

| Input | Required | Description |
|---|---:|---|
| `--release` / `-r` | Yes | Release path/name in Artifactory. |
| `--statusxml` | Yes | Local XML file to update. |
| Environment variables | Yes, indirectly | Artifactory connection details are used by `Dap::Artifactory::Tx`. |

Output

- Logs to Mojo::Log. - Updated status.xml. - Exits with status 1 if release is not found.

3.2 Artifactory deployment/downloader

File: `lib/Dap/Artifactory.pm`

This file is intended to define the main deployment/downloader class. However, there is an important typo:

package Dap::Artifatory {

The package name is missing the c in Artifactory. This means the file path lib/Dap/Artifactory.pm does not define Dap::Artifactory; it defines Dap::Artifatory. Unless the rest of the system uses that typo, this module will not load as expected.

This module is intended to:

1. Process JBoss. 2. Process Tomcat. 3. Process WebLogic. 4. Process JDKs.

The run method in lib/Dap/Artifactory.pm does:

$self->process_jboss;
$self->process_tomcat;
$self->process_weblogic;
$self->process_jdks;

It has a force_download flag:

has force_download => ( is => 'rw', default => 0 );

The comments say:

> This option means: ignore staging copy, download remote copy, remove local copy, extract remote copy

Product processing

process_weblogic, process_tomcat, and process_jboss all call _process_product.

The _process_product method:

1. Iterates over products from the manifest. 2. Filters by technology. 3. Skips if already installed or staged unless force_download is enabled. 4. Checks if the artifact exists in Artifactory. 5. Downloads it. 6. For WebLogic: - Moves the archive to the WebLogic tarball staging directory. 7. For Tomcat/JBoss: - Extracts it under the staging application directory. - Removes the downloaded temporary file.

The relevant code in lib/Dap/Artifactory.pm:

if ($self->tx->file_exist($p->af_file)) {
    my $downloaded = $self->tx->download(file => $p->af_file, archive => $p->archive);

    if ($p->is_weblogic) {
        rename($downloaded, $p->absolute_staging_name)
            ? $self->log->info("$downloaded -> ".$p->absolute_staging_name)
            : $self->log->error("Failed to move $downloaded - $!");
    }
    else {
        $self->tx->extract(dir => $p->verison, file => $p->archive);
        unlink($downloaded)
            ? $self->log->debug("Removing temporary file $downloaded")
            : $self->log->error("Failed to cleanup $downloaded - $!");
    }
}

There is a typo here:

$p->verison

should probably be:

$p->version

JDK processing

process_jdks in lib/Dap/Artifactory.pm:

1. Iterates over manifest JDKs. 2. Skips JDKs where location is unknown. 3. Skips already installed/staged JDKs unless force_download is enabled. 4. Checks the JDK archive in Artifactory. 5. Downloads and extracts it. 6. Deletes the temporary archive.

3.3 Artifactory transaction/client layer

File: `lib/Dap/Artifactory/Tx.pm`

Dap::Artifactory::Tx wraps Artifactory operations.

It uses:

use Mojo::UserAgent;
use Dap::Const;
use Dap::Cluster;

It expects Artifactory credentials and base URL from environment variables:

has user  => ( is => 'rw', lazy => 1, default => $ENV{ARTIFACTORY_USER}  );
has token => ( is => 'rw', lazy => 1, default => $ENV{ARTIFACTORY_TOKEN} );
has url   => ( is => 'rw', lazy => 1, default => $ENV{ARTIFACTORY_URL}   );

The default Artifactory repository path is:

/dist-private-local/com/db/dap

as defined in lib/Dap/Artifactory/Tx.pm:

has default_artifatory_path => (
    is      => 'rw',
    default => sub { '/dist-private-local/com/db/dap' }
);

Note the attribute name is also misspelled as default_artifatory_path.

HTTP authentication

Mojo::UserAgent is configured so that every request gets credentials:

$tx->req->url->userinfo(sprintf("%s:%s", $self->user, $self->token));
$tx->req->url->base(Mojo::URL->new($self->url));

File existence check

file_exist in lib/Dap/Artifactory/Tx.pm calls Artifactory’s storage API:

my $url = $self->url . "/api/storage/dist-private-local/com/db/dap/$file";
$self->ua->get($url)->res->is_success;

So if called with:

$self->tx->file_exist('weblogic/weblogic-14.1.1.0.240628.tar.bz2')

it checks:

$ARTIFACTORY_URL/api/storage/dist-private-local/com/db/dap/weblogic/weblogic-14.1.1.0.240628.tar.bz2

Download

The download method deliberately shells out to curl instead of using Mojo::UserAgent.

The comment in lib/Dap/Artifactory/Tx.pm says:

> For some funny reason Mojo::UserAgent does not download entire file from Artifactory therefore the need to shell out to external curl command to download the archive.

The generated command is approximately:

curl -OJkLu <user>:<token> <ARTIFACTORY_URL>/<path>/<file>

The path defaults to:

/dist-private-local/com/db/dap

The file is downloaded into $const->dap_tmp.

Extract

The extract method in lib/Dap/Artifactory/Tx.pm extracts tar archives into the staging apps directory:

/bin/tar --no-same-owner --extract --auto-compress \
  --directory=<staging_apps> \
  --file=<dap_tmp>/<archive>

Before extraction, it removes any existing staging directory:

rm -fr <staging_apps>/<dir>

Upload

The upload method in lib/Dap/Artifactory/Tx.pm PUTs a file to Artifactory using Mojo::UserAgent.

4.1 Manifest class

File: `lib/Dap/Artifactory/Manifest.pm`

Dap::Artifactory::Manifest is the main metadata updater.

It requires:

has release => ( is => 'rw', required => 1 );

It also has a status_xml path, defaulting to:

$const->dap_etc . '/status.xml'

The XML document is loaded lazily:

XML::LibXML->load_xml(location=>$self->status_xml)

The release URL is computed by:

return $self->tx->url . $self->tx->default_artifatory_path . '/' . $self->release;

So for release foo, the tool expects metadata under:

$ARTIFACTORY_URL/dist-private-local/com/db/dap/foo

Update sequence

update in lib/Dap/Artifactory/Manifest.pm does:

$self->update_products;
$self->update_jdks;
$self->update_jdklist;
$self->save;

So it rewrites three sections of status.xml:

1. <products> 2. <jdks>/<jdk> 3. <jdks>/<jdklist>

Remote release metadata files

Dap::Artifactory::Manifest expects at least three remote resources:

1. Products metadata

Fetched from:

<release-url>/products

in products:

my $aref = $self->tx->ua->get($self->_release_url . '/products')->res->json;

Expected format: JSON array.

Each product entry is converted to Dap::Artifactory::Manifest::Entry::Product, defined in lib/Dap/Artifactory/Manifest/Entry/Product.pm.

Required product fields are:

upgradedue
name
technology
securitystatus
status
jdklist
version

Optional:

releasedate

2. JDK metadata

Fetched from:

<release-url>/java/jdks

in jdks:

my $res = $self->tx->ua->get($self->_release_url . '/java/jdks')->res;
return c(@{$res->json})

Expected format: JSON array.

Each entry is converted to Dap::Artifactory::Manifest::Entry::Jdk, defined in lib/Dap/Artifactory/Manifest/Entry/Jdk.pm.

Required JDK fields are:

location
upgradedue
securitystatus
status
name
version

3. JDK list metadata

Fetched from:

<release-url>/java/jdklist

in jdklist:

my $content = $self->tx->ua->get($self->_release_url . '/java/jdklist')->res->body;

Expected format: plain text.

Blank lines and comments are ignored:

->grep(sub { not m/^\s*$/ and not m/^\s*\#/ })

Each non-comment line is split as:

my ($list, $jdk) = split(/[\s\t]+/);

Example intended format:

java87 java8-openjdk-401
java87 java8-openjdk-402
java1187 java11-openjdk-21

Each line becomes a Dap::Artifactory::Manifest::Entry::Jdklist, defined in lib/Dap/Artifactory/Manifest/Entry/Jdklist.pm.

Local `status.xml` structure

From lib/Dap/Artifactory/Manifest.pm, the expected XML structure is approximately:

<cluster>
  <products>
    <dap ... />
    <tomcat ... />
    <jboss ... />
  </products>

  <jdks>
    <jdk ... />

    <jdklist name="java87">
      <jdk name="java8-openjdk-401" priority="1" />
      <jdk name="java8-openjdk-402" priority="2" />
    </jdklist>
  </jdks>
</cluster>

Product XML update

update_products removes the existing <products> node under <cluster> and recreates it:

my $node = c($dom->findnodes("//cluster"))->first;
c($node->getChildrenByTagName('products'))->map(sub { $_->unbindNode });

my $products = $dom->createElement('products');

For each remote product, it creates an XML element named after the product technology:

my $el = $dom->createElement($p->technology);

Then sets attributes:

upgradedue
name
technology
securitystatus
status
jdklist
version

JDK XML update

update_jdks removes all existing <jdk> children under <cluster>/<jdks> and adds remote JDKs back.

The generated JDK XML elements have attributes:

name
version
location
status
securitystatus
upgradedue

JDK list XML update

update_jdklist removes all existing <jdklist> children under <cluster>/<jdks> and recreates them based on remote java/jdklist.

5.1 Product entry

File: `lib/Dap/Artifactory/Manifest/Entry/Product.pm`

Dap::Artifactory::Manifest::Entry::Product represents one application server/product entry.

Required fields:

upgradedue
name
technology
securitystatus
status
jdklist
version

Optional:

releasedate

It uses constants from lib/Dap/Artifactory/Const.pm.

WebLogic detection

sub is_weblogic {
    my $self = shift;
    return $self->technology eq WEBLOGIC;
}

Since WEBLOGIC is defined as dap in lib/Dap/Artifactory/Const.pm, WebLogic product entries are expected to have:

"technology": "dap"

Archive naming

For WebLogic:

weblogic-<version>.tar.bz2

For other products, it is intended to be:

<version>.tar.gz

But there is a bug in lib/Dap/Artifactory/Manifest/Entry/Product.pm:

: $_->version . '.tar.gz';

This should almost certainly be:

: $self->version . '.tar.gz';

As written, $_ may be undefined or not what is intended.

Artifactory file paths

For WebLogic:

weblogic/weblogic-<version>.tar.bz2

For others:

<technology>/<version>.tar.gz

Examples:

weblogic/weblogic-14.1.1.0.240628.tar.bz2
tomcat/tomcat-9.0.71.tar.gz
jboss/jboss-eap-7.4.17.tar.gz

5.2 JDK entry

File: `lib/Dap/Artifactory/Manifest/Entry/Jdk.pm`

Dap::Artifactory::Manifest::Entry::Jdk represents one JDK.

Required fields:

location
upgradedue
securitystatus
status
name
version

Archive naming:

sub archive { shift->location . '.tar.gz' }

Artifactory path:

sub af_file { 'java/' . shift->archive }

So a JDK with location:

openjdk1.17.0_10

maps to:

java/openjdk1.17.0_10.tar.gz

Installation and staging paths are based on $const, from external Dap::Const:

absolute_install_name => $const->dap_apps . '/' . location
absolute_staging_name => $const->staging_apps . '/' . location

5.3 JDK list entry

File: `lib/Dap/Artifactory/Manifest/Entry/Jdklist.pm`

Dap::Artifactory::Manifest::Entry::Jdklist is a small value object with:

list
name
priority

It represents one mapping from a JDK list name to a JDK name, for example:

java87 java8-openjdk-401

where:

list     => 'java87'
name     => 'java8-openjdk-401'
priority => 1

File: `lib/Dap/Artifactory/Const.pm`

Dap::Artifactory::Const exports technology constants:

sub WEBLOGIC { 'dap' };
sub TOMCAT   { 'tomcat' };
sub JBOSS    { 'jboss' };
sub JAVA     { 'java' };

The important detail is that WebLogic is represented as technology value:

dap

not:

weblogic

This matters in lib/Dap/Artifactory/Manifest/Entry/Product.pm, lib/Dap/Artifactory.pm, and lib/Dap/Artifactory/Dapfile.pm.

7.1 Dispatcher

File: `lib/Dap/Artifactory/Dapfile.pm`

Dap::Artifactory::Dapfile is intended to read a .dap JSON file from a directory.

process($dir) sets:

$self->file(path("$dir/.dap"));

If the file does not exist, it logs an error.

If it exists:

1. Slurps JSON. 2. Reads the technology key. 3. Normalizes it with lc(trim(...)). 4. Sets a selector for XML lookup: - tomcattomcat - dap / WebLogic → dap - jbossjboss 5. Constructs a Dap::Artifactory::Dapfile::Generic object.

The selector logic in lib/Dap/Artifactory/Dapfile.pm:

$meta->{selector} =
    $meta->{technology} eq TOMCAT   ? 'tomcat' :
    $meta->{technology} eq WEBLOGIC ? 'dap'    :
    $meta->{technology} eq JBOSS    ? 'jboss'  :
    undef;

The embedded examples after __END__ in lib/Dap/Artifactory/Dapfile.pm show intended JSON metadata for JDK and Tomcat entries.

Example Tomcat metadata:

{
    "technology":"tomcat",
    "name":"Tomcat 9.0.71",
    "status":"Unsupported",
    "version":"tomcat-9.0.71",
    "jdklist":"java1187",
    "securitystatus":"Insecure High",
    "upgradedue":"2023-09-12",
    "releasedate":"2023-06-12"
}

There is also a JDK example, but it contains invalid JSON syntax:

"location"="openjdk1.17.0_10"

should be:

"location": "openjdk1.17.0_10"

7.2 Generic `.dap` processor

File: `lib/Dap/Artifactory/Dapfile/Generic.pm`

Dap::Artifactory::Dapfile::Generic updates an XML status file based on .dap metadata.

Required attributes:

jdklist
name
releasedate
securitystatus
status
technology
upgradedue
version
selector

It currently uses a hard-coded XML file:

my $filename = '/home/nedevala/status.xml';

The commented line suggests the intended file was:

$const->dap_etc . '/status.xml'

It loads the XML:

my $dom = XML::LibXML->load_xml(location=>$filename);

Then searches for an existing product node:

my $tc = c($dom->findnodes("//cluster/products/".$self->selector))
    ->grep(sub{$_->{version} eq $self->version})
    ->first;

If found, it updates these attributes:

jdklist
securitystatus
status
upgradedue

If not found, it creates a new element under <cluster>/<products> with attributes:

name
status
version
jdklist
securitystatus
upgradedue

It then pretty-prints and writes the XML file.

This is a narrower, local-metadata-based alternative to the release-manifest approach in lib/Dap/Artifactory/Manifest.pm.

7.3 JDK `.dap` processor

File: `lib/Dap/Artifactory/Dapfile/Jdk.pm`

Dap::Artifactory::Dapfile::Jdk defines a data model for JDK .dap metadata.

Required fields:

technology
name
priority
jdklist
version
location
status
securitystatus
upgradedue
releasedate

However, the process method is empty:

sub process {
    my $self = shift;
}

So JDK-specific .dap processing is not implemented yet.

The example after __END__ in lib/Dap/Artifactory/Dapfile/Jdk.pm again shows the intended JDK metadata shape, but also contains the same invalid JSON syntax for location.

File: `lib/Dap/Artifactory/PrettyPrint.pm`

Dap::Artifactory::PrettyPrint is a vendored copy of XML::LibXML::PrettyPrint.

It provides utilities to:

1. Strip insignificant whitespace. 2. Indent XML nodes. 3. Pretty-print XML documents before saving.

It is used by:

- lib/Dap/Artifactory/Manifest.pm - lib/Dap/Artifactory/Dapfile/Generic.pm

For example, in lib/Dap/Artifactory/Manifest.pm:

Dap::Artifactory::PrettyPrint->new->pretty_print($self->dom);

and in lib/Dap/Artifactory/Dapfile/Generic.pm:

Dap::Artifactory::PrettyPrint->new->pretty_print($dom);

The purpose is to keep status.xml human-readable after programmatic modifications.

File: `list.json`

list.json is an example Artifactory storage API response for the WebLogic directory:

dist-private-local/com/db/dap/weblogic

It contains:

{
  "uri": "https://artifactory.intranet.db.com/artifactory/api/storage/dist-private-local/com/db/dap/weblogic",
  "files": [...]
}

Each file entry includes:

{
  "uri": "/weblogic-14.1.1.0.240628.tar.bz2",
  "size": 1428937446,
  "lastModified": "2024-08-05T12:24:44.530+02:00",
  "folder": false,
  "sha1": "...",
  "sha2": "..."
}

This confirms the archive naming convention used by lib/Dap/Artifactory/Manifest/Entry/Product.pm:

weblogic-<version>.tar.bz2

For example, list.json contains:

/weblogic-14.1.1.0.240628.tar.bz2
/weblogic-14.1.1.0.231220.tar.bz2
/weblogic-12.2.1.4.231010.tar.bz2

This matches the WebLogic archive method in lib/Dap/Artifactory/Manifest/Entry/Product.pm:

'weblogic-' . $self->version . '.tar.bz2'

Based on all files, the project can be specified as follows.

10.1 Product/release management purpose

The system manages binary releases stored in Artifactory and updates destination hosts’ metadata and staging areas.

It supports these technology classes:

| Technology | Constant | XML element | Archive format |
|---|---|---|---|
| WebLogic | `WEBLOGIC` = `dap` | `<dap>` | `weblogic-<version>.tar.bz2` |
| Tomcat | `TOMCAT` = `tomcat` | `<tomcat>` | `<version>.tar.gz` |
| JBoss | `JBOSS` = `jboss` | `<jboss>` | `<version>.tar.gz` |
| Java/JDK | `JAVA` = `java` | `<jdk>` | `<location>.tar.gz` |

The constants are defined in lib/Dap/Artifactory/Const.pm.

10.2 Artifactory connection specification

Defined mainly in lib/Dap/Artifactory/Tx.pm.

Required environment variables:

| Variable | Description |
|---|---|
| `ARTIFACTORY_USER` | Artifactory username |
| `ARTIFACTORY_TOKEN` | Artifactory API token/password |
| `ARTIFACTORY_URL` | Artifactory base URL |

Default repository path:

/dist-private-local/com/db/dap

Storage API check path:

<ARTIFACTORY_URL>/api/storage/dist-private-local/com/db/dap/<file>

Download path:

<ARTIFACTORY_URL>/dist-private-local/com/db/dap/<file>

10.3 Release layout in Artifactory

A release should live under:

/dist-private-local/com/db/dap/<release>

The following files are expected:

<release>/products
<release>/java/jdks
<release>/java/jdklist

`<release>/products`

JSON array.

Each object should contain:

{
  "technology": "tomcat",
  "name": "Tomcat 9.0.71",
  "status": "Unsupported",
  "version": "tomcat-9.0.71",
  "jdklist": "java1187",
  "securitystatus": "Insecure High",
  "upgradedue": "2023-09-12",
  "releasedate": "2023-06-12"
}

Required by lib/Dap/Artifactory/Manifest/Entry/Product.pm:

upgradedue
name
technology
securitystatus
status
jdklist
version

`<release>/java/jdks`

JSON array.

Each object should contain:

{
  "name": "java8-openjdk-401",
  "version": "17.0.10",
  "location": "openjdk1.17.0_10",
  "status": "Invest",
  "securitystatus": "Secure",
  "upgradedue": "2025-12-31"
}

Required by lib/Dap/Artifactory/Manifest/Entry/Jdk.pm:

location
upgradedue
securitystatus
status
name
version

`<release>/java/jdklist`

Plain text.

Example:

# list-name jdk-name
java87 java8-openjdk-401
java87 java8-openjdk-402
java1187 java11-openjdk-21

Each non-comment line maps a JDK list to a JDK name. Priority is assigned based on order within the same list, as implemented in lib/Dap/Artifactory/Manifest.pm.

10.4 `status.xml` update behavior

Implemented in lib/Dap/Artifactory/Manifest.pm.

Given a valid release and local XML file:

1. Load status.xml. 2. Remove existing <products> under <cluster>. 3. Create a new <products> element. 4. Add one child element per product. 5. Remove existing <jdk> children under <cluster>/<jdks>. 6. Add one <jdk> element per remote JDK. 7. Remove existing <jdklist> children under <cluster>/<jdks>. 8. Add one <jdklist> per remote JDK list. 9. Pretty-print the XML. 10. Save status.xml.

10.5 Installation/staging behavior

Implemented in lib/Dap/Artifactory.pm and lib/Dap/Artifactory/Tx.pm.

For each product from the manifest:

1. If already installed, skip. 2. If already staged, skip. 3. Otherwise, check Artifactory. 4. Download the archive into $const->dap_tmp. 5. If WebLogic: - Move the archive into $const->staging_wltarballs. 6. If Tomcat/JBoss: - Extract the archive into $const->staging_apps. - Delete temporary archive.

For each JDK:

1. Ignore entries where location is unknown. 2. If already installed, skip. 3. If already staged, skip. 4. Otherwise, check Artifactory. 5. Download the archive. 6. Extract into $const->staging_apps. 7. Delete temporary archive.

If force_download is enabled, the installed/staged checks are bypassed.

11.1 Command-line inputs

From artifactory-release:

--release / -r
--statusxml

Example:

artifactory-release \
  --release release-2024-08 \
  --statusxml /opt/dap/etc/status.xml

11.2 Environment inputs

From lib/Dap/Artifactory/Tx.pm:

ARTIFACTORY_USER
ARTIFACTORY_TOKEN
ARTIFACTORY_URL

11.3 Local filesystem inputs

From lib/Dap/Artifactory/Manifest.pm:

status.xml

From lib/Dap/Artifactory/Dapfile.pm:

<directory>/.dap

From lib/Dap/Artifactory.pm and lib/Dap/Artifactory/Tx.pm, paths provided by external Dap::Const, such as:

$const->dap_tmp
$const->dap_apps
$const->dap_etc
$const->staging_apps
$const->staging_wltarballs
$const->prog_curl

Dap::Const is not provided in the submitted documents, but it is heavily relied on.

11.4 Remote Artifactory inputs

From lib/Dap/Artifactory/Manifest.pm:

<release>/products
<release>/java/jdks
<release>/java/jdklist

From lib/Dap/Artifactory/Manifest/Entry/Product.pm and lib/Dap/Artifactory/Manifest/Entry/Jdk.pm, expected artifact locations include:

weblogic/weblogic-<version>.tar.bz2
tomcat/<version>.tar.gz
jboss/<version>.tar.gz
java/<location>.tar.gz

list.json demonstrates the real Artifactory WebLogic directory layout.

12.1 Updated XML

The main output of artifactory-release and lib/Dap/Artifactory/Manifest.pm is an updated status.xml.

12.2 Downloaded/staged binaries

The main output of lib/Dap/Artifactory.pm and lib/Dap/Artifactory/Tx.pm is staged software under paths from Dap::Const, for example:

$const->staging_apps/<product-version>
$const->staging_apps/<jdk-location>
$const->staging_wltarballs/weblogic-<version>.tar.bz2

12.3 Logs

All major classes use Mojo::Log.

Examples:

- artifactory-release - lib/Dap/Artifactory.pm - lib/Dap/Artifactory/Tx.pm - lib/Dap/Artifactory/Dapfile.pm - lib/Dap/Artifactory/Dapfile/Generic.pm - lib/Dap/Artifactory/Manifest.pm

13.1 Package typo in `lib/Dap/Artifactory.pm`

The file declares:

package Dap::Artifatory {

but the expected package from the path is:

Dap::Artifactory

This will cause load/use issues unless all callers use the misspelled name.

13.2 `Dap::Artifactory::Manifest` requires `release`, but `lib/Dap/Artifactory.pm` constructs it without one

In lib/Dap/Artifactory/Manifest.pm:

has release => ( is => 'rw', required => 1 );

But in lib/Dap/Artifactory.pm:

has manifest => (
    is      => 'rw',
    default => sub { Dap::Artifactory::Manifest->new }
);

That will fail because release is required.

Either release needs to be supplied, or the manifest class should support a local-only mode.

13.3 Product archive bug

In lib/Dap/Artifactory/Manifest/Entry/Product.pm:

: $_->version . '.tar.gz';

should almost certainly be:

: $self->version . '.tar.gz';

13.4 Typo in product extraction

In lib/Dap/Artifactory.pm:

$self->tx->extract(dir => $p->verison, file => $p->archive);

should be:

$self->tx->extract(dir => $p->version, file => $p->archive);

13.5 Artifactory existence checks in `Manifest.pm` appear inconsistent with release paths

In lib/Dap/Artifactory/Manifest.pm, products checks:

$self->tx->file_exist('products')

but then fetches:

$self->_release_url . '/products'

Similarly, jdks checks:

$self->tx->file_exist('java/jdks')

but fetches:

$self->_release_url . '/java/jdks'

That means it checks:

/dist-private-local/com/db/dap/products
/dist-private-local/com/db/dap/java/jdks

but fetches:

/dist-private-local/com/db/dap/<release>/products
/dist-private-local/com/db/dap/<release>/java/jdks

The check probably should include $self->release.

13.6 Hard-coded status XML path

In lib/Dap/Artifactory/Dapfile/Generic.pm:

my $filename = '/home/nedevala/status.xml';

This should likely be configurable or use:

$const->dap_etc . '/status.xml'

as suggested by the commented code.

13.7 Invalid example JSON

In both lib/Dap/Artifactory/Dapfile.pm and lib/Dap/Artifactory/Dapfile/Jdk.pm, the sample JDK JSON contains:

"location"="openjdk1.17.0_10"

This should be:

"location": "openjdk1.17.0_10"

13.8 `download` does not check `curl` return code

In lib/Dap/Artifactory/Tx.pm, download runs:

system(@cmd);

but does not verify success. It then renames and returns the expected path regardless.

A production version should check:

system(@cmd) == 0

and validate the downloaded file exists and has non-zero size.

13.9 Potential unsafe shell usage

In lib/Dap/Artifactory/Tx.pm:

system("@cmd");

is used for rm -fr.

This invokes the shell. It would be safer to use list form:

system(@cmd);

or a Perl filesystem library.

13.10 `update_jdks` appends the node inside the attribute loop

In lib/Dap/Artifactory/Manifest.pm:

for my $attr (sort qw/name version location status securitystatus upgradedue/) {
    $jdk->setAttribute($attr => $_->$attr);
    $jdklist_node->appendChild($jdk);
}

The append should probably happen after the loop:

for my $attr (...) {
    $jdk->setAttribute($attr => $_->$attr);
}
$jdklist_node->appendChild($jdk);

Appending the same node repeatedly will not normally duplicate it, but it is semantically wrong and confusing.

13.11 No XML structural validation

lib/Dap/Artifactory/Manifest.pm assumes these nodes exist:

<cluster>
  <jdks>
  </jdks>
</cluster>

If they are missing, calls like:

$jdklist_node->getChildrenByTagName('jdk')

will fail.

13.12 No JSON schema validation

Remote files such as products, java/jdks, and .dap are assumed to contain valid JSON with all required fields. Moo required attributes will catch missing fields, but error reporting may not be user-friendly.

A more formal version of the intended tool could be:

Command

artifactory-release --release RELEASE --statusxml STATUS_XML

Preconditions

1. ARTIFACTORY_USER, ARTIFACTORY_TOKEN, and ARTIFACTORY_URL are set. 2. STATUS_XML exists and has a <cluster> root or descendant containing <products> and <jdks>. 3. Artifactory contains:

/dist-private-local/com/db/dap/RELEASE/products
/dist-private-local/com/db/dap/RELEASE/java/jdks
/dist-private-local/com/db/dap/RELEASE/java/jdklist

Behavior

1. Validate that the release exists. 2. Load remote product list. 3. Load remote JDK list. 4. Load remote JDK-list mappings. 5. Rewrite relevant sections of STATUS_XML. 6. Pretty-print and save STATUS_XML.

Deployment behavior

A separate deployment command or object should:

1. Load the same release manifest. 2. For each product and JDK: - Determine expected Artifactory archive. - Determine local install/staging paths. - Skip if already installed/staged unless forced. - Download missing archives. - Extract or stage them.

Artifact naming

WebLogic: weblogic/weblogic-<version>.tar.bz2
Tomcat:   tomcat/<version>.tar.gz
JBoss:    jboss/<version>.tar.gz
JDK:      java/<location>.tar.gz

This naming is confirmed by lib/Dap/Artifactory/Manifest/Entry/Product.pm, lib/Dap/Artifactory/Manifest/Entry/Jdk.pm, and the sample Artifactory listing in list.json.

| File | Role |
|---|---|
| `artifactory-release` | CLI script. Validates release exists and updates `status.xml`. |
| `lib/Dap/Artifactory.pm` | Intended main downloader/stager for WebLogic, Tomcat, JBoss, and JDKs. Contains package-name typo. |
| `lib/Dap/Artifactory/Dapfile.pm` | Reads `.dap` JSON metadata and dispatches to a processor. |
| `lib/Dap/Artifactory/Dapfile/Generic.pm` | Updates product entries in `status.xml` from `.dap` metadata. Uses hard-coded XML path. |
| `lib/Dap/Artifactory/Dapfile/Jdk.pm` | Placeholder model for JDK `.dap` metadata. Processing not implemented. |
| `lib/Dap/Artifactory/PrettyPrint.pm` | Vendored XML pretty-printer used before saving XML. |
| `lib/Dap/Artifactory/Tx.pm` | Artifactory transaction layer: existence check, download, upload, extract. |
| `lib/Dap/Artifactory/Manifest.pm` | Core release manifest reader and `status.xml` updater. |
| `lib/Dap/Artifactory/Manifest/Entry/Product.pm` | Product metadata object and archive/path naming logic. Contains bug using `$_`. |
| `lib/Dap/Artifactory/Manifest/Entry/Jdk.pm` | JDK metadata object and archive/path naming logic. |
| `lib/Dap/Artifactory/Manifest/Entry/Jdklist.pm` | JDK-list mapping object. |
| `lib/Dap/Artifactory/Const.pm` | Defines technology constants. |
| `list.json` | Example Artifactory storage API response for WebLogic artifacts. Confirms archive naming and repository layout. |
version 1