7. Deployment View

The system has no server side of its own. It is a build-time tool: it runs wherever Maven runs and its only runtime dependency is a reachable Confluence Cloud instance.

Infrastructure Level 1

  ┌─────────────────────────────────────────┐
  │ Build environment (developer or CI)     │
  │                                         │
  │  JDK 21   Maven ≥ 3.9.2   Node 24 (*)   │
  │                                         │
  │  ┌───────────────┐   ┌───────────────┐  │
  │  │ Antora build  │──►│ Maven plugin  │  │
  │  │ (site → HTML) │   │ atlassian:    │  │
  │  └───────────────┘   │ publish       │  │
  │                      └───────┬───────┘  │
  └──────────────────────────────┼──────────┘
                                 │ HTTPS
                                 ▼
                   ┌──────────────────────────┐
                   │ Confluence Cloud         │
                   │ *.atlassian.net/wiki     │
                   └──────────────────────────┘

  (*) only in the module that generates the Antora site
Element Explanation

JDK 21

Required by the compiled bytecode of all three artifacts.

Maven ≥ 3.9.2

Runs the plugin. The floor is inherited from the release tooling, see 2. Architecture Constraints.

Node 24 / Antora 3.1

Only needed where the site is generated. Installed into the build by frontend-maven-plugin, so no global Node installation is required. A project that already has its site elsewhere does not need Node at all.

Confluence Cloud

The target. Reached over HTTPS with the credentials of the configured account; the account’s permissions define what the tool may do.

Deployment of the Artifacts

Three artifacts are published to Maven Central under the group io.github.huber-and.atlassian. Three GitHub Actions workflows drive the deployments:

Workflow Trigger Result

build.yml

Push and pull request on main

Builds and tests, then deploys a -SNAPSHOT to the Central Portal snapshot repository.

publish.yml

A GitHub release is created

Builds with -Prelease -Drevision=<tag> -Dchangelist= and deploys the release to Maven Central.

pages.yml

Push on main touching architecture-docs/

Builds this documentation with Antora and deploys it to GitHub Pages.

confluence.yml

Push on main touching architecture-docs/ or any published module

Builds all modules and publishes this documentation to the Confluence space.

The version is assembled from ${revision}${changelist} (CI friendly versions). A release build overrides both from the Git tag name, so the tag is the version — there is no version string to forget in a POM.

Signing and upload run through central-publishing-maven-plugin; distributionManagement is deliberately absent, since the plugin routes both snapshots and releases.

Deployment of the Documentation

The documentation goes to two places, from one source.

To GitHub Pages

pages.yml builds the Antora site and publishes it to GitHub Pages. This path does not involve Maven at all — it needs no JDK, only Node and Antora — and it uses playbook.yml.

Two settings in that playbook are load-bearing rather than decorative:

worktrees: false

Makes Antora read the commit instead of the working tree. Without it, Antora ignores edit_url and emits a file:// link into the CI runner’s file system on every page. It also means Pages shows the state of main, not of a dirty checkout.

site.url

Must carry the repository path segment (https://huber-and.github.io/atlassian-tools), otherwise sitemap, canonical and 404 URLs point at the domain root. Setting it is also what makes Antora emit sitemap.xml and 404.html in the first place.

GitHub Pages has to be enabled once in the repository settings with GitHub Actions as the source. Until then, the deploy job fails with "Pages site not found" — the workflow is correct, the repository is not yet configured.

To Confluence

The same source, published by the system itself. The result is public — the Architecture space — which makes the Antora site and the Confluence pages directly comparable:

mvn -pl architecture-docs clean compile atlassian:publish

compile runs Antora, which writes the site to target/docs/docs/main; atlassian:publish picks it up and pushes it into the configured space. Both steps live in the same POM, so the published state can never be newer or older than the built site.

In CI the same two steps run in confluence.yml. The build step installs into the local repository rather than merely packaging, because the publish runs as a separate Maven invocation and can only resolve the plugin from there. It skips everything that exists only for publishing to Maven Central — signing, sources and javadoc. Skipping the signature is not optional: the GPG plugin is bound to the verify phase of the default build, so a plain install fails in CI with no default secret key. The signing key belongs to the artifact release workflows and is deliberately absent here.

The credentials follow one constraint that shapes the whole workflow: username and password have no CLI property, so they cannot be passed as -D arguments — see 8. Cross-cutting Concepts. setup-java therefore writes a settings.xml containing a <server id="confluence"> entry whose values come from the environment, and the build points serverId at that entry. The connection URL is kept as a repository secret as well — not because it is confidential (a Confluence cloud id is served unauthenticated from /_edge/tenant_info) but to keep all three connection values in one place.

Which URL form works depends on the token type, not on the account type:

  • A classic API token authenticates against the site form https://<site>.atlassian.net/wiki.

  • A scoped API token must use the platform API gateway, https://api.atlassian.com/ex/confluence/<cloudId>/wiki. Atlassian documents the site form as explicitly not working for scoped tokens.

Service accounts can only create scoped tokens, so for them the gateway form is the only option — which is why the CONFLUENCE_URL secret holds that form. A normal user account issuing a scoped token is bound by the same rule.

The cloud id is not confidential; read it from https://<site>.atlassian.net/_edge/tenant_info.

Basic authentication keeps working: the account e-mail as the user and the token as the password. A Bearer header would work too, but the publisher uses basic auth.

Scoped tokens also need the right scopes. Beyond reading spaces and reading and writing pages, this publisher needs attachment access, content-property access for its content hash, and — if deleteOrphans stays enabled — permission to delete pages. A token missing one of them fails at exactly the operation that needs it, not at connect time.

The workflow also triggers on changes to wiki-publisher, wiki-client and the Maven plugin, not just on documentation changes: a change to the publisher changes the result in Confluence even when the source stays the same. Because unchanged pages are skipped by their content hash, such a run is cheap — and it doubles as a continuous integration test of the publisher against a real Confluence instance, which is the gap listed as D1 in 11. Risks and Technical Debt.

<mapper>
    <spaceKey>AT</spaceKey>
    <root>Architecture</root>
    <path>${project.basedir}/target/docs/docs/main</path>
</mapper>
The path points into target, which means a clean build always publishes a freshly generated site. Publishing a stale site is only possible by skipping compile on purpose.

Credentials are not part of this configuration. serverId names a <server> entry in settings.xml, and the plugin decrypts its password through Maven’s own mechanism — see 8. Cross-cutting Concepts.