Skip to content

Plugin Release Policy

This page defines the release standard for each marketplace that Appcircle publishes CI/CD distribution plug-ins to. It covers versioning, the release trigger, pre-release/beta availability, and prerequisites. It is module-agnostic: it applies to every current and future Appcircle plug-in published to that marketplace, regardless of product line. For the development and testing lifecycle of an individual plug-in, see TDD-0012.

Marketplace Ecosystem Packaging Host org
RubyGems Fastlane RubyGem appcircleio
GitHub Marketplace GitHub Actions Node action (committed dist/) appcircleio
Jenkins Update Center Jenkins HPI jenkinsci
Visual Studio Marketplace Azure DevOps VSIX appcircleio

General principles (all marketplaces)

  • Version source of truth is the git tag (vX.Y.Z / X.Y.Z). Tagging is manual. CI never invents a version.
  • A release must include all intended changes. Publish from the default branch (or a release branch merged into it), never from a partial or feature branch.
  • Beta must never shadow production. A normal install resolves the latest stable. Betas require explicit opt-in, on the marketplaces that support them.
  • Pre-release testing is mandatory. Exercise the locally built package end-to-end before publishing. See the recipe per marketplace below, and the full lifecycle in TDD-0011.
  • Releases run in CI using a stored marketplace credential. Never release from a developer's machine with ad-hoc tokens.
  • Self-hosted parity: every plug-in accepts optional authEndpoint / apiEndpoint, defaulting to the Appcircle cloud (backward compatible). TLS is always verified. Self-signed and private-CA servers require their CA trusted by the runner.

RubyGems (Fastlane)

Packaging & version: RubyGem. The version comes from the latest git tag vX.Y.Z. The release script rewrites version.rb at publish time. Tagging is manual.

Release mechanism: a Jenkins Multibranch Pipeline (Jenkinsfile runs pipeline.sh, which runs ci/release.rb), executed inside the repo's Dockerfile.ci image, with the RubyGems API key exposed as the GEM_HOST_API_KEY Jenkins credential. It is branch-gated, so a build publishes at most once.

Standard flow:

  1. Push git tag vX.Y.Z (manual). This fixes the version.
  2. Push to release/x.y.z to publish a pre-release x.y.z.beta.N. N auto-increments from the betas already on RubyGems for that base version.
  3. Merge to the default branch to publish the production x.y.z. This is idempotent and skips if that version is already on RubyGems.

Beta: ✅ supported. RubyGems pre-releases are dotted (x.y.z.beta.N, not npm-style -beta.1) and are hidden unless the consumer opts in (gem install <gem> --pre, or an exact --version). gem install and fastlane add_plugin always resolve the latest stable.

Pre-release test: path-link the locally built gem into a throwaway Fastlane project (gem '<plugin>', path: '...' in the Pluginfile) and run a lane end-to-end before tagging.


GitHub Marketplace (GitHub Actions)

Packaging & version: a Node action whose committed dist/ bundle is what consumers run (the action does not build at consume time). The version is the latest stable git tag vX.Y.Z. The developer pushes vX.Y.Z manually, and CI derives beta tags from it.

Release mechanism: a native .github/workflows/release.yml using the built-in GITHUB_TOKEN (no external credential, and free on public repos). It is branch-gated.

Standard flow:

  1. Rebuild and commit dist/ whenever src/ changes. The published action runs the committed bundle.
  2. Push git tag vX.Y.Z (manual) to set the stable version.
  3. Push to release/* for a pre-release: CI tags vX.Y.Z-beta.N (auto N) and creates a prerelease GitHub Release. The major alias (vX) is not moved.
  4. Merge to the default branch for production: CI publishes the stable vX.Y.Z GitHub Release and moves the major alias vX to it.

Beta: ✅ supported. Pre-releases are published as prerelease GitHub Releases (vX.Y.Z-beta.N). releases/latest stays on the last stable, so consumers only get a beta by pinning that exact tag.

Guards: - A pre-push tag hook (.githooks/pre-push, enabled per clone via git config core.hooksPath .githooks) rejects tags that are not vX.Y.Z / vX.Y.Z-<pre>. - release.yml runs a build smoke test but not a byte-for-byte dist check. ncc --source-map embeds the build path, so the bundle is not byte-reproducible across machines. Keeping dist/ fresh is the developer's responsibility (step 1).

Pre-release test: run the locally built bundle before tagging, either with uses: ./ in a throwaway workflow on a branch, or with act locally.


Jenkins Update Center (Jenkins)

Jenkins is the most involved marketplace. The plug-in lives under the jenkinsci org (not appcircleio), the release infrastructure is owned by the Jenkins community, and publishing is gated by a one-time onboarding. We contribute code via PRs and trigger releases, but never touch community secrets. The model is the official Continuous Delivery (CD) of Plugins (JEP-229). It is not maven-release-plugin, and not release branches.

Packaging & version: an HPI built with Maven. The version is pom.xml's ${revision}${changelist}. Set <revision> to the target X.Y.Z (<changelist> stays -SNAPSHOT during development). The CD tooling produces the release version from ${revision}. The git tag is the trigger, not the version source, so keep the tag equal to <revision>.

One-time onboarding (per repo)

  1. Hosting under jenkinsci: the repo must be transferred or created under the jenkinsci org via the Jenkins hosting process (a prerequisite for Update Center publishing).
  2. Maintainers in RPU: in jenkins-infra/repository-permissions-updater, the file permissions/plugin-<artifactId>.yml lists the developers. The username is the accounts.jenkins.io / Jira username (lowercase), not necessarily the GitHub username. Each maintainer must have:
  3. an accounts.jenkins.io account,
  4. logged in at least once to Jira (issues.jenkins.io) and Artifactory (repo.jenkins-ci.org),
  5. 2FA enabled on their GitHub account.
  6. Add cd.yaml: commit .github/workflows/cd.yaml to the plugin repo. It delegates to the official reusable workflow:
    jobs:
      maven-cd:
        uses: jenkins-infra/github-reusable-workflows/.github/workflows/maven-cd.yml@v1
        secrets:
          MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
          MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }}
    
  7. Enable CD via an RPU PR: open a PR to repository-permissions-updater adding cd:\n enabled: true to the plugin's permissions file (and ensure github: points at the correct repo and the developers list is current). On merge, the RPU bot auto-provisions MAVEN_USERNAME / MAVEN_TOKEN as encrypted GitHub Actions secrets in the plugin repo. Do not create these by hand (MAVEN_TOKEN is a real Artifactory token that the bot generates).
  8. The RPU PR runs ci.jenkins.io validation. A common failure is User name not known to Jira: <user>, which means that maintainer hasn't logged into Jira, or the case is wrong (use the exact lowercase Jira username). Fix and re-push.

Per-release flow (once onboarded)

  1. Land the code on the default branch (master) via PR (jenkinsci branch protection blocks direct pushes).
  2. Set the version: bump pom.xml <revision> to the target X.Y.Z.
  3. Trigger the release. cd.yaml is triggered by workflow_dispatch (a maintainer runs it from the Actions tab) and/or a push of a tag (X.Y.Z matching <revision>). maven-cd builds and deploys to repo.jenkins-ci.org (Artifactory). The Jenkins Update Center picks it up within a few hours.

No release/x.y.z branches (not idiomatic for Jenkins). The maintainer triggers the public release. Appcircle's internal Jenkins is never involved.

Beta: ❌ none. The Update Center has no RubyGems-style opt-in beta channel (the experimental Update Center is not used).

Pre-release test: build the HPI locally and run it against a test instance. Use mvn hpi:run (dev Jenkins with the plug-in loaded), or mvn package plus Manage Jenkins → Plugins → Advanced → Deploy Plugin, or a JenkinsRule integration test that runs a real job. Never publish a beta to the Update Center.


Visual Studio Marketplace (Azure DevOps)

Packaging & version: a VSIX task extension built with tsc. The version comes from the latest git tag X.Y.Z. pipeline.sh derives it (git describe --tags) and injects it into task.json and vss-extension.json before packaging, so the manifests always match the tag. Tagging is manual.

Release mechanism: a Jenkins pipeline runs pipeline.sh, which packages with tfx and publishes the public extension. The publish token is the repo's existing Jenkins credential AzureDevOpsPluginPublishToken (PUBLISH_TOKEN).

Standard flow:

  1. Push git tag X.Y.Z (manual).
  2. Merge to main. pipeline.sh injects the tag version into the manifests and publishes the public extension via tfx extension publish.

Beta: ❌ none. The Visual Studio Marketplace has no RubyGems-style opt-in prerelease channel (versions are plain MAJOR.MINOR.PATCH), so there is no public beta.

Pre-release test: because there is no beta channel, validate before publishing public. Build the .vsix locally (tfx extension create) and install it into a test organization (private or shared extension), then run it in a pipeline. Only promote to the public extension once verified.

Production publishes from the default branch, main.