0012-External CI/CD Distribution Plug-ins: Technical Design Document¶
TL;DR
Appcircle ships eight external CI/CD distribution plug-ins across four ecosystems: Fastlane, GitHub Actions, Jenkins, Azure DevOps. Each ecosystem has two product lines: Testing Distribution (TD) and Enterprise App Store (EAS). All of them do the same two jobs against the same public API. They exchange a Personal API Token (PAT) for a Bearer token, upload a build artifact, poll the task, and (for EAS) publish. They differ only in the host platform's packaging, manifest, and marketplace conventions. This document is the single reference for the full lifecycle: development, local testing, beta/pre-release, and production validation. It documents each plug-in with the same structure. The release mechanism differs per ecosystem and is not yet standardized. For the verified per-ecosystem reality and the target standard, see Plugin Release Policy.
A third product line, Publish (app store publishing), is being added across the same four ecosystems as separate plug-ins. Unlike TD/EAS, it can upload a binary and/or trigger the profile's publish flow; it is documented in its own Publish Module section below.
Status: π Draft β’ Linear: BE-8868 (analysis) β’ Team: Backend β’ Project: Marketplace Plugins
Introduction¶
Purpose: The development, local testing, beta/pre-release, and production validation of Appcircle's external CI/CD distribution plug-ins is currently scattered across idoc (releases/plugins) notes and the public marketplace docs, and is not described in sufficient detail anywhere. This TDD consolidates that knowledge into one authoritative reference for contributors and maintainers.
Scope:
- External CI/CD distribution plug-ins only: Fastlane, GitHub Actions, Jenkins, Azure DevOps. Each ships in two product lines, Testing Distribution (TD) and Enterprise App Store (EAS).
- Per plug-in: (1) development (repo structure, code layout, required files, conventions), (2) local testing, (3) beta/pre-release (release branch flow, versioning, tagging), (4) production validation (post-release verification).
- No new authorization scopes. Documents the existing Personal API Token auth model and the self-hosted endpoint configuration (authEndpoint / apiEndpoint).
- No UI surface, activity-log, webhook, or notification changes. This is a documentation deliverable.
System Overview¶
High-Level Description: Every plug-in implements the same two-phase contract against the Appcircle public API:
- Authenticate: exchange the user-supplied PAT for a short-lived Bearer access token at
POST {authEndpoint}/auth/v{1|2}/token(bodyapplication/x-www-form-urlencoded,pat=<PAT>). - Distribute: upload the artifact, poll
GET /task/v1/tasks/{taskId}until completion, and for EAS optionally publish.
The plug-ins are thin clients. All business logic lives server-side in Appcircle. The differences between the eight repos are limited to host-platform packaging (gem, Node action, Jenkins HPI, Azure DevOps VSIX), manifest format, and the target marketplace.
Inventory & current versions¶
| Ecosystem | Product | Repository | Default branch | Marketplace | Version |
|---|---|---|---|---|---|
| Fastlane | TD | appcircleio/fastlane_plugin_appcircle_testing_distribution |
main |
RubyGems | 0.2.2 |
| Fastlane | EAS | appcircleio/fastlane-plugin-appcircle_enterprise_app_store |
main |
RubyGems | 0.0.1 |
| GitHub Actions | TD | appcircleio/appcircle-testing-distribution-githubaction |
main |
GitHub Marketplace | 0.0.2 |
| GitHub Actions | EAS | appcircleio/appcircle-enterprise-app-store-githubaction |
main |
GitHub Marketplace | 0.0.2 |
| Jenkins | TD | jenkinsci/appcircle-testing-distribution-plugin |
master |
Jenkins Update Center | 1.11-SNAPSHOT |
| Jenkins | EAS | jenkinsci/appcircle-enterprise-app-store-plugin |
master |
Jenkins Update Center | 1.9-SNAPSHOT |
| Azure DevOps | TD | appcircleio/appcircle-azuredevops-testdist-task-extension |
main |
Visual Studio Marketplace | task 0.1.0 / pkg 0.0.70 |
| Azure DevOps | EAS | appcircleio/appcircle-azuredevops-entstore-task-extension |
main |
Visual Studio Marketplace | task 0.1.0 / pkg 0.0.70 |
Jenkins plug-ins live under the
jenkinsciorg (notappcircleio). This is a prerequisite for publishing to the Jenkins Update Center.
Architecture¶
System Architecture (data flow):
CI/CD step (Fastlane / GH Action / Jenkins / Azure DevOps task)
β PAT
βΌ
POST {authEndpoint}/auth/v{1|2}/token βββΊ auth.appcircle.io β access_token
β Authorization: Bearer <access_token>
βΌ
ββ Testing Distribution βββββββββββββββββββββββββββββββββββββββββββββββ
GET/POST /distribution/v2/profiles (resolve / create profile)
POST /distribution/v2/profiles/{id}/app-versions (multipart File + Message) β taskId
ββ Enterprise App Store βββββββββββββββββββββββββββββββββββββββββββββββ
POST /store/v2/profiles/app-versions (multipart File) β taskId
PATCH /store/v2/profiles/{id}/app-versions/{vid}?action=publish (summary, releaseNotes, publishType)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
GET /task/v1/tasks/{taskId} βββΊ poll until completed / failed
Technology Stack:
| Ecosystem | Language / runtime | Packaging | Build / bundle |
|---|---|---|---|
| Fastlane | Ruby >= 2.6 |
RubyGem | bundler / rake; dep rest-client |
| GitHub Actions | TypeScript, Node 20 | Action (dist/index.js) |
@vercel/ncc; deps @actions/core, axios, form-data |
| Jenkins | Java, plugin-parent 4.85, jenkins.version 2.440.3 |
HPI | Maven; deps json-api, apache-httpcomponents-client-4-api |
| Azure DevOps | TypeScript, Node 16 handler | VSIX | tsc + tfx-cli; deps azure-pipelines-task-lib, axios |
External Dependencies:
- Appcircle Auth (
auth.appcircle.io/auth/vN/token), Distribution API (/distribution/v2), Store API (/store/v2), Task API (/task/v1). - Marketplaces: RubyGems, GitHub Marketplace, Jenkins Update Center, Visual Studio Marketplace.
- Build agents: macOS 14 (arm64), Ubuntu 22.04, RHEL 9 where applicable. Jenkins supports Linux (build matrix: Linux JDK 21 + Windows JDK 17).
Auth version drift: Fastlane and Jenkins call
/auth/v2/token; GitHub Actions and Azure DevOps call/auth/v1/token. Both work today; new development should standardize onv2.
Key Components¶
Authentication (Personal API Token): all plug-ins¶
- Purpose: Authenticate to Appcircle without storing long-lived credentials.
- Responsibilities: Take the user-supplied PAT, exchange it for a Bearer access token, attach
Authorization: Bearer <token>to all subsequent calls. - Interactions:
POST {authEndpoint}/auth/v{1|2}/token. - Security: No new scopes. Jenkins stores the PAT encrypted as
hudson.util.Secret; GitHub Actions / Azure DevOps rely on platform secret masking.
Self-hosted endpoint configuration: Azure DevOps only (today)¶
- Purpose: Point the plug-in at a self-hosted Appcircle instance.
- Responsibilities: Optional
authEndpoint(defaulthttps://auth.appcircle.io) andapiEndpoint(defaulthttps://api.appcircle.io) inputs override the cloud hosts. - Status: Implemented only in the Azure DevOps extensions. Fastlane, GitHub Actions and Jenkins hardcode the cloud hosts; adding these two parameters is the documented path to bring them to parity.
Parameter matrix¶
Testing Distribution
| Parameter | Required | Note |
|---|---|---|
personalAPIToken |
β | PAT |
profileName |
β | distribution profile |
createProfileIfNotExists |
β | default false |
appPath |
β | artifact path (.ipa, .apk, .aab, .zip/.xcarchive) |
message |
varies | required in Fastlane/GH; optional in Azure DevOps |
authEndpoint / apiEndpoint |
β | Azure DevOps only |
Enterprise App Store
| Parameter | Required | Note |
|---|---|---|
personalAPIToken |
β | PAT |
appPath |
β | artifact path (.ipa, .apk; Azure DevOps EAS also .aab) |
summary |
β | store summary |
releaseNotes |
β | store release notes |
publishType |
varies | 0=None / 1=Beta / 2=Live; optional (default None) in GH |
authEndpoint / apiEndpoint |
β | Azure DevOps only |
Detailed Design: Per-Plug-in Lifecycle¶
Each plug-in is documented with the same four headings: Development, Local Testing, Beta / Pre-release, Production Validation.
Fastlane¶
Development
lib/fastlane/plugin/<plugin>/
version.rb # VERSION constant (single source of truth)
<plugin>.rb # module + all_classes loader
actions/<plugin>_action.rb # Fastlane::Actions::<Name>Action
helper/ # auth + upload services
<plugin>.gemspec # spec.version = ...::VERSION
Gemfile / Rakefile / .rubocop.yml
fastlane/Fastfile, fastlane/Pluginfile
Jenkinsfile / pipeline.sh / ci/release.rb / Dockerfile.ci # release automation (see Beta / Pre-release)
- Action class extends
Fastlane::Action; parameters declared asFastlaneCore::ConfigItemwithenv_namebindings (AC_PERSONAL_API_TOKEN,AC_PROFILE_NAME,AC_APP_PATH,AC_MESSAGE, β¦). - The only runtime dependency is
rest-client.rubygems_mfa_required = 'false'. TD platforms[:ios, :android]; EAS all. - Self-hosted ready: both gems accept optional
authEndpoint/apiEndpoint(envAC_AUTH_ENDPOINT/AC_API_ENDPOINT). When unset they default to the Appcircle cloud, so existing cloud users are unaffected. If the self-hosted server uses a self-signed or private-CA certificate, trust that CA on the machine running Fastlane (for example,SSL_CERT_FILEpointing at a PEM bundle). The gem does not disable TLS verification.
Local Testing
bundle install
bundle exec rake # spec + rubocop (default task)
bundle exec rake spec # rspec only
bundle exec rake rubocop # lint only
Wire the local checkout into a real Fastlane project to run end-to-end:
# test app fastlane/Pluginfile
gem 'fastlane-plugin-appcircle_testing_distribution', path: '/path/to/local/checkout'
# Fastfile
lane :smoke do
appcircle_testing_distribution(
personalAPIToken: ENV['AC_PERSONAL_API_TOKEN'], profileName: 'QA',
createProfileIfNotExists: true, appPath: 'build/app.ipa', message: 'local smoke test'
)
end
EAS has a SimpleCov-enabled placeholder
spec/; TD has no spec suite yet (gap).
Beta / Pre-release
Release runs on a Jenkins Multibranch Pipeline (Jenkinsfile β pipeline.sh β ci/release.rb, all executed inside the Dockerfile.ci Ruby image; RubyGems credential exposed as GEM_HOST_API_KEY via the Jenkins credential Appcircle-Fastlane-RubyGems-Cred). It is branch-gated, so a build never double-publishes:
- Version source of truth = the latest git tag (
vX.Y.Z). Tagging is manual: the developer pushesgit tag vX.Y.Z; the pipeline never creates tags.version.rbis rewritten by the release script at publish time. - Push to
release/x.y.zβpipeline.sh prereleaseβ publishes a RubyGems pre-releasex.y.z.beta.N.Nauto-increments from the betas already on RubyGems for that base version. - Merge to
mainβpipeline.sh productionβ publishes the productionx.y.z; idempotent (skips if that version is already on RubyGems).
RubyGems beta semantics: a pre-release version is dotted (
x.y.z.beta.N, not npm-style-beta.1) and is hidden fromgem install/fastlane add_pluginunless the user opts in. There is no dist-tag channel.
See Plugin Release Policy β Fastlane.
Production Validation
Test the published gem (not just local code), and verify the beta does not shadow production:
- Production:
gem install fastlane-plugin-appcircle_<...>/fastlane add_plugin appcircle_<...>must resolve the latest stable version. - Beta: only an explicit opt-in pulls it:
gem install <gem> --pre(or an exact--version x.y.z.beta.N). Confirm a normal install still resolves the prod version.
Then run a lane against a real Appcircle profile and confirm: upload + task completion, every previously-documented parameter still accepted (backward compatible), the new authEndpoint/apiEndpoint work against a self-hosted instance and default to cloud when unset, and the README documents the new behavior.
GitHub Actions¶
Development
action.yml # name, branding, inputs, runs.using: node20, main: dist/index.js
src/index.ts # entry β run()
src/main.ts # input parsing + orchestration
src/api/authApi.ts # PAT β token
src/api/uploadApi.ts # upload + poll (+ publish for EAS)
dist/index.js # ncc-bundled output, COMMITTED to the repo
package.json / tsconfig.json / script/release
- Runtime
node20; bundled with@vercel/ncc.action.ymlinputs mirror the parameter matrix; TD brandingblue, EASorange. - GitHub runs the committed
dist/. Regenerate and commit it wheneversrc/changes.
Local Testing
npm install
npm run package # ncc build src/index.ts -o dist (regenerate bundle)
npm test # jest (no test files yet: gap)
npm run lint
test.yml) that runs the action via uses: ./; EAS has no CI workflow (gap).
- For full local execution use act or a throwaway branch workflow.
Beta / Pre-release
β οΈ No release branches and no publish automation today. script/release only creates a vX.Y.Z tag and pushes it. There is no GitHub Release / Marketplace publish step, no moving major tag (v1), and no check-dist guard. dist/ is usually rebuilt on a feature branch, so the tagged commit's dist/ and package.json can drift. See Plugin Release Policy β GitHub Actions for the verified state and target standard.
Production Validation
Reference the action by published tag in a test workflow; confirm upload + task completion, old inputs still resolve, and new inputs appear in the Marketplace listing.
Jenkins¶
Development
pom.xml # artifactId, ${revision}${changelist}, parent 4.85, jenkins.version 2.440.3
src/main/java/io/jenkins/plugins/appcircle/.../<Name>Builder.java
src/main/java/.../AuthService.java, UploadService.java, Models/
src/main/resources/.../<Builder>/config.jelly # UI form
Jenkinsfile # buildPlugin matrix (Linux JDK21 / Windows JDK17)
.github/workflows/ # TD: cd.yaml + jenkins-security-scan.yml; EAS: release-drafter.yml + jenkins-security-scan.yml (no cd.yaml)
- Main class extends
Builder implements SimpleBuildStep;@Symbol("appcircleTestingDistribution" | "appcircleEnterpriseAppStore")enables pipeline DSL. - Parameters bound via
@DataBoundConstructor/@DataBoundSetter; PAT stored encrypted ashudson.util.Secret. Cloud-only today. - artifactIds:
appcircle-testing-distribution,appcircle-enterprise-store.
Local Testing
mvn hpi:run # dev Jenkins with the plugin loaded
mvn test # JenkinsRule tests (only configRoundtrip stubs: gap)
mvn package # build target/<artifactId>.hpi
.hpi via Manage Jenkins β Plugins β Advanced β Deploy Plugin, then validate with a freestyle job and a pipeline step:
appcircleTestingDistribution(
personalAPIToken: env.AC_PAT, profileName: 'QA',
createProfileIfNotExists: true, appPath: 'app.apk', message: 'pipeline test'
)
Beta / Pre-release
Releases use the official Continuous Delivery of Plugins (JEP-229) on master (not maven-release-plugin, no release branches). The version is pom.xml's ${revision}${changelist}; set <revision> to the target X.Y.Z, keeping the git tag equal to <revision>. A maintainer triggers the release through cd.yaml, which delegates to the jenkins-infra reusable maven-cd workflow (workflow_dispatch from the Actions tab, or a pushed X.Y.Z tag) and deploys to repo.jenkins-ci.org; the Update Center picks it up within a few hours. TD already ships cd.yaml; EAS has no cd.yaml yet (only release-drafter), so onboarding it to CD is the prerequisite for an automated deploy. See Plugin Release Policy β Jenkins.
Production Validation
Install from the Update Center on a clean Jenkins; run freestyle + pipeline jobs (including a Linux agent); confirm config-roundtrip, old job configs still load, and new features present.
Azure DevOps¶
Development
vss-extension.json # publisher Appcircle, version, files, categories
<taskfolder>/task.json # id (UUID), inputs, execution Node16 -> index.js
<taskfolder>/index.ts # entry point (TD: buildandreleasetask/, EAS: task/)
configs/dev.json # id ...-dev, public:false
configs/release.json # id ..., public:true
package.json # tsc build, tfx-cli; CI via pipeline.sh + Jenkinsfile
- TD task
AppcircleTestingDistribution(folderbuildandreleasetask/, targetes6); EAS taskAppcircleEnterpriseStore(foldertask/, targetes2022). - Inputs include the standard matrix plus
authEndpoint/apiEndpoint, which is the self-hosted hook. Compiled withtsc(no bundler).
Local Testing
yarn install
(cd <taskfolder> && yarn install)
yarn package # tsc ./<taskfolder>/index.ts -> index.js
tfx extension create --manifest-globs vss-extension.json --overrides-file configs/dev.json
.vsix into a test org and run it in a pipeline. No unit-test framework is wired up (gap), so validation is integration-based.
Beta / Pre-release
β
This is the one ecosystem that follows a release-branch β main flow end-to-end. pipeline.sh publishes the private -dev extension from release/* (beta) and the public extension from main. The version is bumped on the release branch (task.json + vss-extension.json), merged to main, tagged, then merged back to develop. β οΈ Branch naming is inconsistent (release/0.1.0 semver vs release/6031 numeric id). See Plugin Release Policy β Azure DevOps.
Production Validation
Add the task to a test pipeline; exercise both cloud and a self-hosted endpoint via authEndpoint/apiEndpoint; confirm upload + task completion + (EAS) publish, old inputs still resolve, and publishType options render.
Publish Module¶
A third product line, Publish (app store publishing), is being added across all four ecosystems as separate plug-ins. Unlike TD/EAS (which only upload/distribute), the Publish plug-in can upload a binary to a Publish profile and/or trigger the profile's publish flow (submitting an app version to the App Store / Google Play). Behavior is identical across the four ecosystems; only host-platform packaging differs (gem, Node action, Azure VSIX, Jenkins HPI).
Repositories & status¶
| Ecosystem | Repo | Status |
|---|---|---|
| Fastlane | fastlane-plugin-appcircle_publish (RubyGems) |
Beta 0.1.0.beta.1 published; dev-verified |
| GitHub Actions | appcircle-publish-githubaction |
dev-verified; beta cut |
| Azure DevOps | appcircle-azuredevops-publish-task-extension |
dev-verified (mock-run) |
| Jenkins | appcircle-publish-plugin β jenkinsci/β¦ |
built + dev-verified; jenkinsci hosting requested |
Parameter matrix¶
| Parameter | Required | Note |
|---|---|---|
personalAPIToken |
β | PAT (Fastlane also accepts personalAccessKey) |
platform |
β | ios / android (Publish profiles are platform-scoped) |
publishProfile |
β | Publish profile name (unique per platform) |
upload |
β | default false; upload the binary as a new app version |
publish |
β | default false; trigger the profile's publish flow |
appPath |
β
if upload |
artifact path (.ipa, .apk, .aab) |
authEndpoint / apiEndpoint |
β | self-hosted endpoints (cloud defaults) |
At least one of upload / publish must be enabled.
Behavior modes¶
upload |
publish |
Behavior |
|---|---|---|
| β | β | Upload appPath as a new app version on the profile. |
| β | β | Trigger the publish flow for the profile's current release candidate. |
| β | β | Upload, mark the new version as release candidate, then publish it. |
| β | β | Error: nothing to do. |
Rules:
- In-progress guard: a new publish is never started if one is already running for the target profile.
- Release candidate: in the upload+publish mode the uploaded version is auto-marked as release candidate; publish-only publishes the profile's existing release candidate.
- Progress: the publish status is polled and each flow step is logged once with a human-readable status label + icon.
API contract (verified on dev)¶
| Purpose | Method | Path |
|---|---|---|
| Resolve profile | GET | /publish/v2/profiles/{platform} (match by name) |
| Upload information | GET | /publish/v1/profiles/{platform}/{id}/app-versions?action=uploadInformation&fileName=&fileSize= |
| Upload binary | PUT / POST | signed uploadUrl (PUT octet-stream, or POST multipart with file field last) |
| Commit upload | POST | /publish/v1/profiles/{platform}/{id}/app-versions?action=commitFileUpload β {taskId} |
| Upload task poll | GET | /task/v1/tasks/{taskId} (stateValue 3=Completed, 2=Canceled) |
| Mark release candidate | PATCH | /publish/v2/profiles/{platform}/{id}/app-versions/{vid}?action=releaseCandidate body {"ReleaseCandidate":true} |
| In-progress check | GET | /build/v1/queue/my-dashboard?page=1&size=1000 (filter publishId != null && profileId == target) |
| Publish steps β run id | GET | /publish/v2/profiles/{platform}/{id}/app-versions/{vid}/publish β steps[0].publishId |
| Start publish | POST | /publish/v2/profiles/{platform}/{id}/publish/{publishId}?action=restart body "{}" |
| Publish status poll | GET | /publish/v1/profiles/{platform}/{id}/app-versions/{vid}/publish β status (0=success, 1=failed, else running) |
v1 vs v2: the signed-URL upload/commit actions live on
publish/v1(thepublish/v2app-versions path ignoresaction=uploadInformationand returns the version list instead). Profile listing and the publish trigger are onpublish/v2. Auth is the same PAT model as TD/EAS (/auth/v1/tokenon GitHub/Jenkins/Azure,/auth/v2/tokenfor PAT on Fastlane).
FlowStepStatus (step-log labels): 0 Success, 1 Failed, 2 Cancelled, 3 Timeout, 90 Waiting, 91 Running, 92 Completing, 99 Unknown, 100 Skipped, 200 Not Started, 201 Stopped, 202 In Progress, 203 Awaiting Response.
CI note: if the profile's publish flow contains a manual-approval step (e.g. "Get Approval via Email"), the flow blocks on it and the plug-in keeps polling until it completes or the poll times out. Prefer flows without manual gates for CI.
Integration and Interfaces¶
External Interfaces:
| Purpose | Default host | Path family |
|---|---|---|
| Auth | https://auth.appcircle.io |
/auth/vN/token |
| API (TD) | https://api.appcircle.io |
/distribution/v2/... |
| API (EAS) | https://api.appcircle.io |
/store/v2/... |
| Task polling | https://api.appcircle.io |
/task/v1/tasks/{taskId} |
- TD flow: resolve profile by
profileName(create ifcreateProfileIfNotExists), upload (POST /distribution/v2/profiles/{id}/app-versions), poll task. - EAS flow: upload (
POST /store/v2/profiles/app-versions), poll task, and ifpublishType != Noneselect the most recent profile (bylastBinaryReceivedDate), thenPATCH .../app-versions/{vid}?action=publish.
Internal Interfaces: None added. The plug-ins are external API clients only.
Deployment and Infrastructure¶
Cloud Deployment¶
No Appcircle Helm/cluster changes. The plug-ins are published to external marketplaces (RubyGems, GitHub Marketplace, Jenkins Update Center, Visual Studio Marketplace). See Plugin Release Policy for the per-marketplace release flow.
Self-Hosted Deployment¶
No Docker Compose / install-script changes. Self-hosted customers configure the Azure DevOps extension's authEndpoint / apiEndpoint inputs to target their instance; bringing self-hosted support to the other three ecosystems requires adding the same two inputs.
Testing and Quality Assurance¶
Test Strategy:
| Integration | Local-test recipe |
|---|---|
| Fastlane | bundle exec rake; path:-link the gem into a test app's Pluginfile and run a lane. |
| GitHub Actions | npm run package; run via uses: ./ on a branch or with act. |
| Jenkins | mvn hpi:run, or mvn package + deploy .hpi; run freestyle + pipeline. |
| Azure DevOps | yarn package + tfx extension create --overrides-file configs/dev.json; install private .vsix and run in a pipeline. |
Release-flow verification: confirm the documented release produces the correct published version on each marketplace (RubyGems install, GH @vX.Y.Z + major tag, Jenkins Update Center pickup, Visual Studio Marketplace public extension validated first via the private -dev).
Post-release checklist (reproducible):
- No regression: a known-good build still uploads and the task completes.
- Backward-compatible params: every previously-documented input still accepted; Jenkins saved job configs still load.
- New features present: new inputs/behaviors appear and function.
- Smoke test: end-to-end against a real Appcircle profile (TD) / store profile + publish (EAS), including a self-hosted endpoint run for Azure DevOps.
Quality Assurance: RuboCop (Fastlane), ESLint/Prettier (GitHub Actions, Azure DevOps), Jenkins security scan + Release Drafter (Jenkins). Test coverage is a known gap across all repos.
Performance Considerations¶
The plug-ins are short-lived CI steps; throughput is not a concern. Two practical limits apply: artifact uploads can be large (up to ~2 GB) and use multipart streaming, and task polling caps at ~100 attempts. No caching or scaling strategy applies.
Maintenance and Support¶
- Monitoring: plug-in execution surfaces in the host CI logs; server-side behavior is observed in the Appcircle backend. No plug-in-side telemetry.
- Support and Maintenance: keep marketplace listings current, regenerate the GitHub Actions
dist/bundle on every source change, and keep Jenkins baseline (jenkins.version) and plugin-parent up to date.
Risks and Mitigation Strategies¶
| Risk | Impact | Mitigation |
|---|---|---|
Stale GitHub Actions dist/ bundle |
Published action runs old code | Enforce npm run package + commit on the tagged commit; add a check-dist CI guard that the bundle matches src/ |
| Thin test coverage (all repos) | Regressions ship undetected | Add real unit tests; rely on the reproducible post-release checklist meanwhile |
| Inconsistent release automation (Fastlane/GH manual & unautomated; Jenkins EAS has no CD; only Azure DevOps fully follows a release-branch flow) | Human error, orphaned release branches, tag/bundle drift | Converge toward the per-marketplace standard in the Plugin Release Policy; note release/x.y.z is not idiomatic for Jenkins (use JEP-229 CD, not release branches) |
Auth version drift (v1 vs v2) |
Divergent behavior, future breakage | Standardize on /auth/v2/token |
| Self-hosted parity (only Azure DevOps) | Self-hosted users blocked on other ecosystems | Add authEndpoint/apiEndpoint inputs to Fastlane, GitHub Actions, Jenkins |
Appendix¶
- Plugin Release Policy: the verified per-ecosystem release mechanism and the target standard.
- Marketplaces: RubyGems Testing Distribution, GitHub Marketplace, Jenkins Update Center, Visual Studio Marketplace (publisher
Appcircle).