This document describes the XVM project's CI/CD pipeline, workflow architecture, and how to use each workflow.
- Pipeline Overview
- Architecture: Build Artifacts vs Releases
- Master Push Flow
- Workflows Reference
- Actions Reference
- Testing Publishing on Non-Master Branches
- Manual Testing
- Version Gating
- Troubleshooting
The XVM CI/CD pipeline follows a clear separation between internal build artifacts and external releases:
┌─────────────────────────────────────────────────────────────────┐
│ PUSH TO MASTER │
│ (version.properties = X.Y.Z-SNAPSHOT) │
└───────────────────────────┬─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ commit.yml (Verify Commit) │
│ ├─ Build XDK │
│ ├─ Run tests (including manual tests) │
│ ├─ Upload artifact: xdk-dist-{COMMIT} │
│ │ • Temporary (10 days) │
│ │ • Contains: xdk-{VERSION}.zip │
│ └─ Trigger publishing (if master or publish-snapshots=true) │
│ └─ gh workflow run with --field ci-run-id={RUN_ID} │
└───────────────────────────┬─────────────────────────────────────┘
│
│ Direct trigger (not workflow_run)
│ Passes ci-run-id for artifact download
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────────┐
│publish-docker│ │homebrew │ │publish-snapshot │
│.yml │ │-update.yml │ │.yml │
├──────────────┤ ├──────────────┤ ├──────────────────┤
│Receive │ │Receive │ │Receive │
│ci-run-id │ │ci-run-id │ │ci-run-id │
│ │ │ │ │ │
│Download │ │Download │ │Download │
│artifact by │ │artifact by │ │artifact by │
│run-id+commit │ │run-id+commit │ │run-id+commit │
│ │ │ │ │ │
│Build Docker │ │Update brew │ │Publish Maven │
│images │ │formula │ │snapshots │
│ │ │ │ │ │
│Push to GHCR │ │Push to tap │ │Publish GitHub │
│ │ │ │ │Release │
└──────────────┘ └──────────────┘ └──────────────────┘
multi-arch xdk-latest xdk-snapshots
amd64/arm64 .rb formula .zip release
-
CI Build (
commit.yml) runs on every push to any branch- Builds, tests, uploads temporary build artifact
- Artifact named:
xdk-dist-{40-char-commit-hash} - On master (or manual with
publish-snapshots=true): Directly triggers publishing workflows
-
Direct Publishing Triggers (master only, or manual with flag):
commit.ymldirectly triggers workflows viagh workflow run --field ci-run-id=...publish-docker.yml- Builds multi-platform Docker imageshomebrew-update.yml- Updates Homebrew tap formulapublish-snapshot.yml- Publishes Maven + GitHub snapshot release- Each workflow receives
ci-run-idto download artifacts from CI run
-
Manual Release (two-phase process):
prepare-release.yml- Creates release branch, stages artifacts, creates PRpromote-release.yml- Promotes staged artifacts to production (auto on PR merge)- See RELEASE_PROCESS.md for complete documentation
Purpose: Pass exact builds between workflows
Storage: GitHub Actions artifacts (10-day retention)
Naming: xdk-dist-{COMMIT}
- Artifact identifier includes full 40-character commit hash
- File inside artifact:
xdk-{VERSION}.zip(from distZip task)
Download: Using actions/download-artifact@v4 with run-id
Example:
- name: Download XDK build artifact
uses: actions/download-artifact@v4
with:
name: xdk-dist-abc123def456... # Full commit hash
path: ./artifacts
repository: ${{ github.repository }}
run-id: ${{ github.event.workflow_run.id }}Purpose: Permanent, public distribution
Storage: GitHub Releases (permanent)
Naming: xdk-{VERSION}.zip (version-based, no commit hash)
Types:
- Snapshot: Overwrites single file in
xdk-snapshotsprerelease - Release: Creates new tagged release
v{VERSION}as DRAFT
Download: Public HTTPS URL
Example URLs:
- Snapshot:
https://github.com/xtclang/xvm/releases/download/xdk-snapshots/xdk-0.4.4-SNAPSHOT.zip - Release:
https://github.com/xtclang/xvm/releases/download/v0.4.4/xdk-0.4.4.zip
When you push to master with version.properties containing a -SNAPSHOT version:
# Example: version.properties
xdk.version=0.4.4-SNAPSHOTSequence:
-
commit.yml starts immediately
├─ Checkout code ├─ Setup Java & Gradle ├─ Build XDK (clean, check, distZip) ├─ Run manual tests (if enabled) └─ Upload artifact: xdk-dist-{commit} -
After CI completes successfully, these run in parallel:
publish-docker.yml:
├─ Download artifact from CI run ├─ Build amd64 image → push to GHCR ├─ Build arm64 image → push to GHCR ├─ Create multi-platform manifests ├─ Test images └─ Clean up old images (keep 10)homebrew-update.yml:
├─ Download artifact from CI run ├─ Calculate SHA256 from artifact ├─ Generate xdk-latest.rb from template ├─ Add dynamic version with timestamp ├─ Clone homebrew-xvm tap ├─ Commit & push formula update └─ Summarypublish-snapshot.yml:
├─ Validate version contains -SNAPSHOT ✓ ├─ Download artifact from CI run ├─ Publish Maven snapshots to GitHub Packages + Maven Central Snapshots ├─ Clean up old Maven packages (keep 50) ├─ Publish GitHub snapshot release (overwrites) └─ Summary
All workflows support workflow_dispatch for manual testing from any branch.
Trigger: Every push, every pull request, manual
Purpose: Build, test, create build artifact
Platforms: Ubuntu (default), Windows (optional via input)
Key Steps:
- Setup XVM project (checkout, versions, Java, Gradle)
- Build XDK (
clean,check,distZip) - Run manual tests inline (configurable)
- Upload artifact:
xdk-dist-{commit}(Ubuntu only) - Generate summary
Manual Trigger Inputs:
publish-snapshots: Trigger publishing workflows after build (default: false)- Set
trueto test full publishing pipeline on non-master branches - Publishing workflows: snapshot, docker, homebrew
- Set
platforms: Run on specific platform(s) or all- Options:
ubuntu-latest,windows-latest,all - Default:
ubuntu-latest
- Options:
extra-gradle-options: Extra Gradle CLI optionsskip-tests: Skip manual tests (default: true)parallel-test-mode: Run manual tests in parallel (default: true)
Manual Test Configuration:
- Set via workflow inputs when manually triggering
- Inline execution to keep cache hot
- Tasks:
runXtc,runOne,runTwoTestsInSequence,runAllTestTasks/runParallel
Example Manual Trigger:
# Build and test only (no publishing)
gh workflow run commit.yml \
--ref your-branch \
-f platforms=ubuntu-latest \
-f skip-tests=false \
-f parallel-test-mode=false
# Build, test, AND trigger publishing workflows
gh workflow run commit.yml \
--ref your-branch \
-f publish-snapshots=true \
-f platforms=ubuntu-latestArtifact Output:
- Name:
xdk-dist-{40-char-commit} - Contains:
xdk-{VERSION}.zip - Retention: 10 days
- Size: ~50-100 MB
Trigger:
- Automatic: After commit.yml completes on master
- Manual: Any branch via workflow_dispatch
Purpose: Publish snapshot artifacts to Maven and GitHub Releases
Version Requirement: MUST contain -SNAPSHOT (validated)
Key Steps:
- Setup XVM project
- Validate version contains -SNAPSHOT (fails if not)
- Determine commit and run-id
- Download build artifact from CI run
- Publish Maven snapshots to GitHub Packages + Maven Central Snapshots
- Clean up old Maven packages:
org.xtclang.xdk(keep 50)org.xtclang.xtc-plugin(keep 50)org.xtclang.xtc-plugin.org.xtclang.xtc-plugin.gradle.plugin(keep 50)
- Publish to GitHub snapshot release (overwrites)
- Generate summary
GitHub Release Behavior:
- Release tag:
xdk-snapshots(prerelease) - Asset name:
xdk-{VERSION}.zip(e.g.,xdk-0.4.4-SNAPSHOT.zip) - Overwrites previous snapshot (always latest)
- Includes commit SHA in release notes
Manual Trigger:
gh workflow run publish-snapshot.yml --ref masterNote: Manual triggers from branches cannot download CI artifacts (only builds from master get artifacts). The workflow will publish Maven snapshots but skip GitHub release.
Trigger:
- Automatic: After commit.yml completes on master
- Manual: Any branch via workflow_dispatch
Purpose: Build and publish multi-platform Docker images
Platforms: linux/amd64, linux/arm64
Key Steps:
- Compute tags (separate job):
- Master:
latest,{VERSION},{COMMIT} - Branch:
{BRANCH},{COMMIT}
- Master:
- Build per architecture (matrix job):
- Download build artifact from CI run
- Copy to Docker context
- Build image for platform
- Push to GHCR with architecture-specific tags
- Create manifests (combines architectures):
- For each base tag, create multi-platform manifest
- Links
{tag}-amd64and{tag}-arm64
- Test images:
- Run
xec --version,xcc --version,xtc --version
- Run
- Clean up (optional):
- Delete old Docker package versions (keep 10)
Manual Trigger Inputs:
skip-tests: Skip Docker image tests (default: false)cleanup: Run cleanup after build (default: true)
Example Manual Trigger:
gh workflow run publish-docker.yml \
--ref master \
-f skip-tests=false \
-f cleanup=truePublished Images:
- Registry:
ghcr.io/xtclang/xvm - Tags (master):
ghcr.io/xtclang/xvm:latestghcr.io/xtclang/xvm:0.4.4-SNAPSHOTghcr.io/xtclang/xvm:abc123def...
- Tags (branch):
ghcr.io/xtclang/xvm:branch-nameghcr.io/xtclang/xvm:abc123def...
Usage:
docker pull ghcr.io/xtclang/xvm:latest
docker run --rm ghcr.io/xtclang/xvm:latest xec --versionTrigger:
- Automatic: After commit.yml completes on master
- Manual: Any branch via workflow_dispatch
Purpose: Update Homebrew tap with latest snapshot
Formula: xdk-latest.rb (class XdkLatest)
Key Steps:
- Setup XVM project (metadata only, no build)
- Determine XDK version (from version.properties or input)
- Determine commit and run-id
- Download build artifact from CI run
- Calculate SHA256 from artifact
- Build release URL pointing to GitHub snapshot release
- Generate dynamic version with timestamp:
- Format:
{VERSION}.{YYYYMMDDHHMMSS} - Example:
0.4.4-SNAPSHOT.20250413120530 - Ensures
brew upgradeworks correctly
- Format:
- Clone homebrew-xvm tap repository
- Copy template
.github/scripts/xdk-latest.rb.template - Replace placeholders with
sed:{{RELEASE_URL}}→ GitHub Release URL{{DYNAMIC_VERSION}}→ Timestamped version{{SHA256}}→ Calculated hash{{JAVA_VERSION}}→ Java version from version.properties
- Commit and push to homebrew-xvm repo
- Generate summary
Manual Trigger Inputs:
xdk-version: Override version (default: use version.properties)
Example Manual Trigger:
gh workflow run homebrew-update.yml --ref masterHomebrew Tap Repository: github.com/xtclang/homebrew-xvm
User Installation:
brew tap xtclang/xvm
brew install xdk-latest
# Upgrade to latest snapshot (choose one):
brew update && brew upgrade xdk-latest # Standard: refresh tap first
brew reinstall xdk-latest # Alternative: always gets latestImportant: Homebrew caches tap metadata locally. You must run brew update to refresh the tap before brew upgrade will detect new snapshot versions. Alternatively, use brew reinstall xdk-latest to always get the latest snapshot.
Dependencies: openjdk@{version} (from version.properties)
Purpose: Two-phase automated release process for XDK releases
Architecture:
- Phase 1: Prepare (
prepare-release.yml) - Build and stage artifacts - Phase 2: Promote (
promote-release.yml) - Promote staged artifacts to production
Understanding Artifact Publishing:
| Artifact Type | Prepare Phase | Promote Phase |
|---|---|---|
| GitHub Packages (Maven) | ✅ Published immediately | No action (already live) |
| Maven Central | ⏸️ Staged in orgxtclang-XXXX |
✅ Close & release to production |
| GitHub Release (zip) | 📝 Uploaded as DRAFT | ✅ Publish draft → public |
| Gradle Plugin Portal | 🔍 Credentials validated | ✅ Published immediately |
For complete release workflow documentation, see: 📖 RELEASE_PROCESS.md
Quick Summary:
-
Prepare Release (Manual trigger):
gh workflow run "Prepare Release" --field release-version=0.4.4- Creates
release/X.Y.Zbranch - Tags
vX.Y.Z - Runs
./gradlew publish(publishes to GitHub Packages + stages to Maven Central) - Uploads XDK zip as GitHub draft release
- Validates Gradle Plugin Portal credentials
- Creates PR to master with next snapshot version
- Creates
-
Review Staged Artifacts (Manual):
- Verify Maven Central staging repository at oss.sonatype.org
- Review GitHub draft release
- Test staged artifacts
- Complete PR checklist
-
Promote Release (Automatic on PR merge):
- Maven Central: Close & release staging → production (via Nexus API)
- GitHub Release: Publish draft → public (via gh CLI)
- Gradle Plugin Portal: Run
./gradlew :plugin:publishPlugins(if enabled) - GitHub Packages: No action (already published in prepare)
Key Features:
- ✅ Automatic version bumps (no manual editing)
- ✅ Staging before production (reversible)
- ✅ PR-based approval gate
- ✅ Single merge = complete release
- ✅ Selective publishing via PR labels
See RELEASE_PROCESS.md for:
- Complete step-by-step instructions
- Selective publishing control
- Manual re-promotion
- Troubleshooting
- Rollback procedures
Trigger: Pull requests to master (Dependabot PRs), manual
Purpose: Validate dependency updates from Dependabot and auto-approve if tests pass
Key Steps:
- Fetch sources and setup project
- Analyze dependency changes
- Generate dependency lock files
- Run validation build (without tests)
- Check for dependency vulnerabilities
- Auto-approve Dependabot PR if all checks pass
Manual Trigger:
gh workflow run "Dependency Updates"Purpose: Complete XVM project setup (checkout, versions, build environment)
Location: .github/actions/setup-xvm-project/action.yml
Inputs:
setup-build: Setup Java/Gradle (default: true)- Set
falsefor metadata-only jobs
- Set
cache-read-only: Gradle cache read-only mode (default: false)- Set
trueto reuse cache from CI build
- Set
checkout-depth: Git checkout depth (default: 1)enable-debug: Enable debug logging (default: false)
Outputs:
java-version: Java version from version.propertiesxdk-version: XDK version (e.g.,0.4.4-SNAPSHOT)xdk-version-release: Without-SNAPSHOT(e.g.,0.4.4)xdk-version-next-snapshot: Patch bumped (e.g.,0.4.5-SNAPSHOT)gradle-version: Gradle version from gradle-wrapper.propertiesjava-distribution: Java distribution (temurin)gradle-options: Standard Gradle CLI optionsgradle-jvm-opts: Standard Gradle JVM options (GRADLE_OPTS)
Steps:
- Fetch sources
- Extract versions from properties files
- Compute release and next snapshot versions
- Compute Kotlin toolchain Java version (main - 1)
- Setup Java for Kotlin toolchain (if setup-build)
- Setup Java (main) (if setup-build)
- Setup Gradle (if setup-build)
- Validate Gradle wrapper (if setup-build)
Usage:
- name: Setup XVM Project
id: versions
uses: ./.github/actions/setup-xvm-project
with:
setup-build: true
cache-read-only: false
enable-debug: falsePurpose: Publish XDK build artifact to GitHub Release
Location: .github/actions/publish-github-release/action.yml
Inputs:
artifact-path: Path to XDK zip file (build artifact)xdk-version: XDK versioncommit: Commit SHA for metadatarepo: Repository in formatowner/repogithub-token: GitHub token with contents:writerelease-type:snapshot(overwrites) orrelease(tagged draft)release-tag: Override release tag (optional)- Defaults:
xdk-snapshotsfor snapshot,v{VERSION}for release
- Defaults:
Outputs:
release-url: URL of published releaseasset-name: Name of published asset
Behavior:
Snapshot Mode (release-type: snapshot):
- Renames artifact to
xdk-{VERSION}.zip - Creates or updates
xdk-snapshotsprerelease - Overwrites previous snapshot asset
- Includes commit SHA in release notes
Release Mode (release-type: release):
- Renames artifact to
xdk-{VERSION}.zip - Creates new tagged release
v{VERSION} - Sets as DRAFT (manual publish required)
- Includes TODO template for release notes
- Sets target commit
Usage:
- name: Publish to GitHub Release
uses: ./.github/actions/publish-github-release
with:
artifact-path: ./artifacts/xdk-0.4.4-SNAPSHOT.zip
xdk-version: 0.4.4-SNAPSHOT
commit: abc123def456...
repo: ${{ github.repository }}
github-token: ${{ secrets.GITHUB_TOKEN }}
release-type: snapshotTo test the complete publishing pipeline (snapshot, Docker, Homebrew) from a non-master branch without merging to master, manually dispatch the Verify Commit workflow. When manually triggered with publish-snapshots=true, Verify Commit automatically triggers all three publishing workflows after successful completion.
gh workflow run commit.yml --ref your-branch-name -f publish-snapshots=trueThis command will:
- Build and test your branch
- Upload build artifact
- Automatically trigger publish-snapshot.yml (with ci-run-id)
- Automatically trigger publish-docker.yml (with ci-run-id)
- Automatically trigger homebrew-update.yml (with ci-run-id)
Without -f publish-snapshots=true, only the build and test run (no publishing).
Trigger Mechanism:
- On master push or manual trigger with
publish-snapshots=true, commit.yml completes its build - At the end of commit.yml, a
trigger-publishingjob runs that:- Checks if this is a release merge (has release tag) - skips if true
- Directly triggers each publishing workflow via
gh workflow run - Passes
ci-run-idfield so workflows can download artifacts from the CI run
- Each publishing workflow receives the
ci-run-idand downloads artifacts directly
Automatic vs Manual Triggering:
| Trigger Type | Branch | publish-snapshots | Publishing Runs? |
|---|---|---|---|
| Push (automatic) | master | N/A | ✅ YES (automatic on master, if not release tag) |
| Push (automatic) | feature-branch | N/A | ❌ NO (branch not master) |
| Manual dispatch | master | false | ❌ NO (flag not set) |
| Manual dispatch | master | true | ✅ YES (flag enabled) |
| Manual dispatch | feature-branch | false | ❌ NO (flag not set) |
| Manual dispatch | feature-branch | true | ✅ YES (flag enabled) |
1. Make changes to your branch:
git checkout -b feature/update-publishing
vim .github/workflows/publish-snapshot.yml
git commit -am "Update snapshot publishing"
git push origin feature/update-publishing2. Trigger Verify Commit (builds + triggers publishing):
gh workflow run commit.yml --ref feature/update-publishing -f publish-snapshots=true3. Monitor workflow runs:
# List recent runs
gh run list --branch feature/update-publishing --limit 10
# Watch specific run
gh run watch4. Check triggered publishing workflows:
- Go to Actions tab in GitHub
- Look for these workflows that started after Verify Commit completed:
- "Publish Snapshots"
- "Publish Docker Images"
- "Update Homebrew"
When you manually trigger Verify Commit from a non-master branch:
✅ Maven Snapshots: Published to GitHub Packages
- Requires version contains
-SNAPSHOT - Safe for testing (snapshots are ephemeral)
✅ GitHub Release: Updates xdk-snapshots prerelease
- Overwrites previous snapshot
- Safe for testing
✅ Docker Images: Published to GHCR
- Tagged with branch name (e.g.,
ghcr.io/xtclang/xvm:feature-update-publishing) - Tagged with commit SHA
- Safe for testing (branch-specific tags)
✅ Homebrew Formula: Updates homebrew-xvm tap
⚠️ Creates real commit in tap repo⚠️ Affects users runningbrew upgrade- Consider if you need to test this
# Just build and test (no publishing)
gh workflow run commit.yml --ref your-branch
# With publishing enabled
gh workflow run commit.yml --ref your-branch -f publish-snapshots=true
# Skip manual tests (faster CI, no publishing)
gh workflow run commit.yml \
--ref your-branch \
-f skip-tests=true
# Run only Ubuntu with publishing
gh workflow run commit.yml \
--ref your-branch \
-f platforms=ubuntu-latest \
-f publish-snapshots=true# 1. Create and checkout feature branch
git checkout -b feature/docker-improvements
git push origin feature/docker-improvements
# 2. Make changes
vim .github/workflows/publish-docker.yml
git commit -am "Optimize Docker builds"
git push
# 3. Test the complete pipeline with ONE command (includes publishing)
gh workflow run commit.yml --ref feature/docker-improvements -f publish-snapshots=true
# 4. Monitor progress (watch until complete)
gh run watch
# 5. Verify all three publishing workflows succeeded
gh run list --branch feature/docker-improvements --limit 10
# 6. If issues found, fix and re-test
vim .github/workflows/publish-docker.yml
git commit -am "Fix Docker build issue"
git push
gh workflow run commit.yml --ref feature/docker-improvements -f publish-snapshots=true
# 7. Once working, merge to master
gh pr create --title "Optimize Docker builds"- Go to:
https://github.com/xtclang/xvm/actions - Click on the running "Verify Commit" workflow
- Wait for it to complete (shows green checkmark)
- Look for triggered workflows below:
- Publish Snapshots - Check it completed successfully
- Publish Docker Images - Verify both amd64 and arm64 built
- Update Homebrew - Confirm formula was updated
You can run multiple times on the same commit:
- Each run is isolated (separate artifact namespace via
run-id) - No conflicts between runs
- Each publishing workflow downloads from its triggering CI run
- Artifacts retained for 10 days
Snapshot Version Required:
publish-snapshot.ymlvalidates version contains-SNAPSHOT- If your
version.propertieshas a non-SNAPSHOT version, snapshot publishing will fail - Docker and Homebrew will still run successfully
Real Publishing:
- This triggers REAL publishing (not a dry-run)
- Maven snapshots → Real GitHub Packages
- Docker images → Real GHCR registry
- Homebrew formula → Real tap repository commit
- GitHub Release → Real
xdk-snapshotsrelease update
When NOT to Use This:
- If you only want to test the build (not publishing), just push and let CI run automatically
- If you want to test release publishing (non-SNAPSHOT), use
publish-release.ymldirectly
Problem: Publishing workflows don't trigger Solution: Check Verify Commit completed successfully. Publishing only triggers on success.
Problem: "Artifact not found" error in publishing workflows Solution: Verify Commit must complete fully and upload artifact. Check the Verify Commit run succeeded.
Problem: Snapshot publishing fails with "not a SNAPSHOT version"
Solution: Your version.properties must contain -SNAPSHOT. Update it or skip snapshot testing.
Method 1: Via GitHub UI
- Go to Actions → Verify Commit workflow
- Click "Run workflow"
- Select branch
- Configure inputs:
platforms: Choose platform(s)test: Enable manual tests (true)parallel-test: Run in parallel (false for sequential)
- Click "Run workflow"
Method 2: Via GitHub CLI
gh workflow run commit.yml \
--ref your-branch \
-f platforms=ubuntu-latest \
-f skip-tests=false \
-f parallel-test-mode=falseManual Test Tasks:
manualTests:runXtc- Run XTC compilermanualTests:runOne -PtestName=TestMisc- Run single testmanualTests:runTwoTestsInSequence- Run two testsmanualTests:runAllTestTasks- Run all tests sequentiallymanualTests:runParallel- Run all tests in parallel
Local Execution:
# Run all tests sequentially
./gradlew manualTests:runAllTestTasks
# Run tests in parallel
./gradlew manualTests:runParallel
# Run single test
./gradlew manualTests:runOne -PtestName=TestMisc
# Run XTC compiler
./gradlew manualTests:runXtcEnvironment Configuration:
// In settings.gradle.kts or gradle.properties
org.gradle.project.includeBuildManualTests=true
org.gradle.project.includeBuildAttachManualTests=truepublish-snapshot.yml validates that version contains -SNAPSHOT:
- name: Validate snapshot version
run: |
VERSION="${{ steps.versions.outputs.xdk-version }}"
if [[ "$VERSION" != *-SNAPSHOT ]]; then
echo "❌ ERROR: Cannot publish snapshots for non-SNAPSHOT version"
exit 1
fiResult: Only SNAPSHOT versions can be published as snapshots
publish-release.yml validates that version does NOT contain -SNAPSHOT:
- name: Validate and determine release version
run: |
if [[ "$RELEASE_VERSION" == *-SNAPSHOT ]]; then
echo "❌ ERROR: Cannot publish release with -SNAPSHOT version"
exit 1
fi
if ! [[ "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "❌ ERROR: Invalid version format"
exit 1
fiResult: Only non-SNAPSHOT semantic versions can be published as releases
Both workflows support version overrides:
Snapshot (automatic from version.properties):
- No override input (always uses version.properties)
- Must contain
-SNAPSHOT
Release (manual input):
workflow_dispatch:
inputs:
version-override:
description: 'Release version override (e.g., 0.4.4)'Use case: Test release workflow without updating version.properties
Symptom: Workflow fails with "Artifact not found: xdk-dist-{commit}"
Causes:
- CI workflow hasn't completed yet
- CI workflow failed
- Manual trigger from branch (artifacts only from master CI runs)
- Artifact expired (10-day retention)
Solution:
- Check CI workflow status for that commit
- For manual testing, trigger from master or use version override
- Check artifact existence:
gh run view {run-id} --log
Symptom: Docker/Homebrew workflow downloads wrong commit
Cause: Race condition or incorrect commit reference
Prevention:
- Workflows use
workflow_run.head_shafor automatic triggers - Full 40-character commit hashes prevent collisions
Verification:
# Check workflow_run event
gh run view {run-id} --json event --jq '.event.workflow_run.head_sha'Symptom: actions/delete-package-versions fails
Causes:
- Insufficient permissions
- Package doesn't exist yet
- Fewer than min-versions-to-keep exist
Solution:
- Verify
packages: writepermission - Ensure packages exist before cleanup
- Check package names are correct
Symptom: brew install xdk-latest fails
Causes:
- Invalid SHA256
- Release URL not accessible
- Syntax error in template
Debugging:
# Check formula syntax
brew audit --strict --online xtclang/xvm/xdk-latest
# Verify download URL
curl -I https://github.com/xtclang/xvm/releases/download/xdk-snapshots/xdk-0.4.4-SNAPSHOT.zip
# Check SHA256
curl -sL {URL} | sha256sumSymptom: publish-github-release action fails
Causes:
- Release tag already exists
- Insufficient permissions
- Invalid artifact path
Solution:
- Check
contents: writepermission - Verify artifact path is correct
- For releases, check if tag already exists:
git tag -l v{VERSION}
Symptom: Docker builds are slow or fail
Causes:
- Cache miss
- Platform-specific cache not found
- Cache corruption
Solution:
# Force cache rebuild
- name: Build Docker image
with:
cache-from: type=gha,scope=${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=${{ matrix.arch }}
no-cache: true # Add this to force rebuildSymptom: Build fails with configuration cache errors
Causes:
- Task captures script object references
- Non-serializable objects in configuration
Solution:
- Use injected services instead of project-level methods
- Follow configuration cache best practices
- Disable temporarily:
GRADLE_OPTIONS="--no-configuration-cache"
- name: Determine commit and run ID
id: commit
shell: bash
run: |
if [ "${{ github.event_name }}" = "workflow_run" ]; then
COMMIT="${{ github.event.workflow_run.head_sha }}"
RUN_ID="${{ github.event.workflow_run.id }}"
else
COMMIT="${{ github.sha }}"
RUN_ID="${{ github.run_id }}"
fi
echo "commit=$COMMIT" >> $GITHUB_OUTPUT
echo "run-id=$RUN_ID" >> $GITHUB_OUTPUT
- name: Download XDK build artifact
uses: actions/download-artifact@v4
with:
name: xdk-dist-${{ steps.commit.outputs.commit }}
path: ./artifacts
github-token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.repository }}
run-id: ${{ steps.commit.outputs.run-id }}jobs:
my-job:
runs-on: ubuntu-latest
# Only run if CI succeeded (for workflow_run) or manual trigger
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}- name: Validate version
run: |
VERSION="${{ steps.versions.outputs.xdk-version }}"
if [[ "$VERSION" != *-SNAPSHOT ]]; then
echo "❌ Wrong version type"
exit 1
fiThe XVM CI/CD pipeline provides:
✅ Automatic snapshot publishing on every master push ✅ Manual release workflow with staging and approval gates ✅ Multi-platform Docker images (amd64, arm64) ✅ Homebrew tap automatically updated with latest snapshots ✅ Maven artifacts published to GitHub Packages and Central ✅ Version gating prevents publishing wrong version types ✅ Artifact tracking with full commit hashes ✅ Clear separation between internal artifacts and external releases
All workflows support manual triggering for testing and emergency releases.