Skip to content
Draft
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
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,36 @@ The startup configuration is cluster-safe: only one node applies the file at a t

A configuration that cannot be read, parsed or fully applied stops the application, so an instance never comes up with a configuration it could not reach - in a rolling deployment this blocks the rollout instead of hiding the failure. Since only successful applies are recorded, the configuration is retried when the instance is started again.

## Plugin installation

The `upm` section installs other plugins declaratively, through the plugin framework itself - no UPM tokens or admin credentials involved. Resolvers and plugins are both keyed maps, so merged YAML documents can override single values. Every plugin references one of the named resolvers explicitly: `marketplace` type resolvers look the artifact up through the Atlassian Marketplace REST API from the plugin key and version alone, `maven` type resolvers derive it from the plugin's Maven coordinates and the standard repository layout.

```yaml
upm:
resolvers:
marketplace:
type: marketplace
baseUrl: https://marketplace.atlassian.com
corp:
type: maven
baseUrl: https://repository.example.com/maven
username: reader
password: secret
plugins:
de.griffel.confluence.plugins.plant-uml:
version: "2026.103"
resolver: marketplace
com.example.internal-plugin:
version: 1.0.0
resolver: corp
groupId: com.example
artifactId: internal-plugin
```

A resolver's base URL may point to a proxying repository such as an Artifactory generic remote: all links returned by the Marketplace API are re-resolved against the resolver's base URL, so the API lookup and the binary download flow through the same repository (resolver credentials work for either type). For endpoints only reachable through a forward proxy, each resolver takes an optional `proxy` (`host`, `port` and optional credentials), so an internal repository and a proxied external one can coexist in the same document.

Plugins already installed in the requested version are skipped, `enabled: false` disables a plugin, and responses echo only the plugins map - never the resolver credentials. Like every other section, `upm` works via the REST API (`PUT /upm` or as part of `_all`) and the startup configuration alike.

## Installation

Download the plugin for your product from the [releases](https://github.com/deftdevs/bootstrapi/releases) and upload it in the product's administration under *Manage apps* → *Upload app*. The endpoints require a user with system administrator permissions.
Expand Down
6 changes: 6 additions & 0 deletions commons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@
<scope>provided</scope>
</dependency>

<dependency>
<groupId>com.atlassian.plugins</groupId>
<artifactId>atlassian-plugins-core</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>com.atlassian.sal</groupId>
<artifactId>sal-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public class BootstrAPI {
public static final String PERMISSION_ANONYMOUS_ACCESS = "anonymous-access";
public static final String PERMISSIONS_GLOBAL = "global";
public static final String PING = "ping";
public static final String PLUGIN = "plugin";
public static final String SESSION_CONFIG = "session-config";
public static final String SETTINGS = "settings";
public static final String SETTINGS_BRANDING = "branding";
Expand All @@ -56,6 +57,9 @@ public class BootstrAPI {
public static final String SETTINGS_GENERAL = "general";
public static final String SETTINGS_SECURITY = "security";
public static final String TRUSTED_PROXIES = "trusted-proxies";
public static final String UPM = "upm";
public static final String UPM_PROXY = "proxy";
public static final String UPM_RESOLVER = "resolver";
public static final String USER = "user";
public static final String USERS = "users";
public static final String USER_PASSWORD = "password";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.deftdevs.bootstrapi.commons.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;

import static com.deftdevs.bootstrapi.commons.constants.BootstrAPI.PLUGIN;

/**
* One plugin to install, keyed by its plugin key in the {@code plugins}
* map: the version plus a reference to the named resolver the artifact is
* fetched from. Marketplace-type resolvers work from the plugin key and
* version alone; Maven-type resolvers additionally need the entry's Maven
* coordinates.
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@XmlRootElement(name = PLUGIN)
public class PluginModel {

@XmlElement
private String version;

/** The key of the resolver in the {@code resolvers} map. */
@XmlElement
private String resolver;

@XmlElement
private String groupId;

@XmlElement
private String artifactId;

/** Defaults to enabled when absent. */
@XmlElement
private Boolean enabled;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.deftdevs.bootstrapi.commons.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;

import static com.deftdevs.bootstrapi.commons.constants.BootstrAPI.UPM_PROXY;

/**
* An optional web proxy for a plugin resolver, for endpoints that are only
* reachable through a forward proxy.
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@XmlRootElement(name = UPM_PROXY)
public class PluginProxyModel {

@XmlElement
private String host;

@XmlElement
private Integer port;

@XmlElement
private String username;

@XmlElement
private String password;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.deftdevs.bootstrapi.commons.model;

import com.deftdevs.bootstrapi.commons.model.type.PluginResolverType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;

import static com.deftdevs.bootstrapi.commons.constants.BootstrAPI.UPM_RESOLVER;

/**
* A named plugin resolver: its type (the Atlassian Marketplace or a Maven
* repository) and the endpoint it works against, either directly or through
* a proxying repository (e.g. an Artifactory generic remote). Credentials
* authenticate against the endpoint itself; an optional web proxy (with its
* own credentials) covers endpoints only reachable through a forward proxy.
* Plugins reference a resolver by its key in the {@code resolvers} map.
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@XmlRootElement(name = UPM_RESOLVER)
public class PluginResolverModel {

@XmlElement
private PluginResolverType type;

@XmlElement
private String baseUrl;

@XmlElement
private String username;

@XmlElement
private String password;

@XmlElement
private PluginProxyModel proxy;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.deftdevs.bootstrapi.commons.model;

import com.deftdevs.bootstrapi.commons.model.type._AllModelStatus;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import java.util.Map;

import static com.deftdevs.bootstrapi.commons.constants.BootstrAPI.UPM;

/**
* Declarative plugin installation: the named resolvers and the plugins to
* install, both keyed maps so that merged YAML documents can override
* single values. Responses echo only the plugins map, never the resolver
* credentials.
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@XmlRootElement(name = UPM)
public class UpmModel {

@XmlElement
private Map<String, PluginResolverModel> resolvers;

@XmlElement
private Map<String, PluginModel> plugins;

@XmlElement
private Map<String, _AllModelStatus> status;

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public abstract class _AbstractAllModel<S> implements _AllModelAccessor {
@XmlElement
private MailServerModel mailServer;

@XmlElement
private UpmModel upm;

@XmlElement
private Map<String, _AllModelStatus> status;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.deftdevs.bootstrapi.commons.model.type;

import com.fasterxml.jackson.annotation.JsonProperty;

/**
* The kind of artifact source a named plugin resolver works against.
*/
public enum PluginResolverType {

@JsonProperty("marketplace")
MARKETPLACE,

@JsonProperty("maven")
MAVEN,

}
Loading