Files
ci-workflows/README.md
T
2026-11-08 07:43:36 -06:00

10 KiB

ci-workflows

Shared Gitea Actions workflows for KOF and QRun repositories. A small, direct port of the CircleCI qqq-orb. Keep it that way.

Maven

Add one file to the repository, .gitea/workflows/build.yaml:

name: build
on:
  push:
jobs:
  maven:
    uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/maven.yaml@v1
    secrets: inherit
    # with:
    #   java-version: '17'
    #   maven-args: -Pui-demo

The repository needs a <revision> property in the root pom.xml.

Every push builds once. PRs use the push run's status on the head commit. Builds run on the container-build runners, which provide a Docker daemon for Testcontainers.

Ref Version Action
feature and other branches, main unchanged mvn verify
develop next minor -SNAPSHOT after a release, else unchanged test, deploy, commit bump
release/X.Y X.Y.0-RC.N test, deploy, commit bump, tag vX.Y.0-RC.N
hotfix/* patch + 1 test, deploy, commit bump, tag vX.Y.Z
tag vX.Y.Z X.Y.Z test, deploy, commit to main
tag publish-* X.Y.Z-<feature>-<sha>-SNAPSHOT test, deploy

Packages go to the owner's Gitea Maven registry, https://git.kof22.com/api/packages/<owner>/maven, using the job token (or an optional PACKAGES_TOKEN secret). Builds resolve dependencies from the QRun-IO registry as well as Maven Central.

Node

.gitea/workflows/build.yaml:

name: build
on:
  push:
jobs:
  node:
    uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/node.yaml@v1
    with:
      node-version: '24'
      commands: |
        npm run lint
        npm test
Input Default Meaning
commands required Shell commands, one per line, run in order; the first failure fails the build
node-version '22' Selects the official node:<version>-bookworm image
install npm ci Dependency install command
uv false Install a pinned, sha256-verified uv for Python script checks
working-directory . Where the install and commands run
runner container-build Runner label
publish false On a release tag, publish after every command passed (below)
version-file package.json The release tag must be v<version> of this file, or the build fails
npm-publish-dirs . Package directories to npm publish, one per line, in order; empty = none
publish-commands empty Commands that publish instead of the npm-publish-dirs loop
release-files empty Files or globs, one per line, for a Gitea release; empty = no release

Publishing

Without publish: true the workflow only verifies. With it, branches still only verify; a tag vX.Y.Z or vX.Y.Z-<pre> (for example v1.2.0-RC.1) publishes:

  1. The tag must equal v + the version-file version. Set the version in package.json (npm version 1.2.0-RC.1 --no-git-tag-version), merge, then tag the merge commit. A mismatch fails the build before anything is installed.
  2. Install and commands run as on any push; a failure stops the release.
  3. Each npm-publish-dirs package goes to the owner's Gitea npm registry, https://git.kof22.com/api/packages/<owner>/npm/, with the PACKAGES_TOKEN org secret (secrets: inherit). A prerelease version gets dist-tag next, a final version latest. The package keeps its name and scope; a publishConfig.registry (npmjs, GitHub) is ignored for the publish. A version that already exists fails: publish a new version instead.
  4. release-files become a Gitea release for the tag (a prerelease for -<pre> versions) with a fresh SHA256SUMS, and the CHANGELOG.md section whose ## heading names the version as its notes. A SHA256SUMS among the files is checked, then replaced. An existing release for the tag fails.
jobs:
  node:
    uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/node.yaml@v1
    secrets: inherit
    with:
      commands: |
        npm test
        npm run build
      publish: true
      # npm-publish-dirs: |          # workspaces, in dependency order
      #   packages/client
      #   packages/cli
      # version-file: packages/cli/package.json
      # release-files: dist/*        # with npm-publish-dirs: '' for a release only

To consume a package:

# .npmrc
@scope:registry=https://git.kof22.com/api/packages/QRun-IO/npm/
//git.kof22.com/api/packages/QRun-IO/npm/:_authToken=${GITEA_TOKEN}

Binaries for macOS or Windows need native runners; repositories that ship them (for example QRun-IO/qqq-cli) keep their own release workflow.

Android

.gitea/workflows/build.yaml (see QRun-IO/qqq-android):

name: build
on:
  push:
    branches: ['**']
    tags: ['v*']
jobs:
  android:
    uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/android.yaml@v1
    secrets: inherit
    with:
      tasks: |
        testDebugUnitTest lintDebug assembleDebug
      emulator-tasks: connectedDebugAndroidTest
Input Default Meaning
tasks build Gradle invocations, one per line (arguments to ./gradlew)
java-version '21' JDK from the image (8, 17, 21, 25)
commands '' Shell commands run before Gradle
node-version '' Install this Node.js (official tarball, checksum-verified) for commands
emulator-tasks '' Gradle invocations run with a booted emulator (needs /dev/kvm)
emulator-image system-images;android-30;google_apis;x86 AVD system image
publish-tasks '' Run on vX.Y.Z tags with -Pversion=X.Y.Z
runner android-build Runner label (Lore builders)

Runs in the pinned ci-android-runtime image (cimg/android plus the emulator). To publish an AAR, declare a maven { name = "gitea" ... credentials(PasswordCredentials::class) } repository whose URL comes from the giteaMavenUrl Gradle property; the workflow sets it to the owner's Gitea Maven registry with PACKAGES_TOKEN (or the job token).

Windows and macOS

Host jobs (no container) on the native runners. The repository's own commands run with the tools the host provides; nothing is installed.

name: build
on:
  push:
    branches: ['**']
    tags: ['v*']
jobs:
  windows:
    uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/windows.yaml@v1
    secrets: inherit
    with:
      commands: |
        dotnet restore App.slnx --locked-mode
        dotnet build App.slnx -warnaserror --no-restore
        dotnet test tests/App.Tests --no-build
      artifacts: artifacts/release/*.msi
  macos:
    uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/macos.yaml@v1
    secrets: inherit
    with:
      commands: |
        swift build
        swift test
Input Windows macOS Meaning
commands PowerShell 7, one script bash -exo pipefail Required. The first failure fails the build
runner kof-windows-amd64 kof-macos-arm64 QRun-IO repositories use qqq-windows-vm6218 / qqq-macos-arm64
dotnet-version '' Fail unless a host SDK starts with this version
xcode '' Select Xcode by version (/Applications/Xcode_<v>.app) or path via DEVELOPER_DIR
lfs false Fetch Git LFS objects (needs git-lfs on the host)
artifacts '' '' Files or globs, one per line; on v* tags they and a SHA256SUMS are attached to the tag's Gitea release
checksums SHA256SUMS SHA256SUMS Checksum asset name; give each platform its own when several jobs release one tag
  • Trigger on push only. Host runners run repository code on the machine, and push events come only from branches of the repository itself, never forks.
  • Windows: native commands with a non-zero exit fail the build. A .ps1 that reports failure with exit 1 must run as pwsh -NoProfile -File x.ps1; called with & it would not stop the script. Test tools that assert on child exit codes themselves (Pester suites) need $PSNativeCommandUseErrorActionPreference = $false first.
  • Releases use PACKAGES_TOKEN (or the job token). Re-running a tag replaces assets of the same name. A Homebrew or Scoop update can read the release's SHA256SUMS.
  • Host limits today: the Windows runner is a batch-logon task with no interactive desktop (no FlaUI UI tests); the macOS runner is a launchd daemon with no GUI session, so xcodebuild test cannot reach testmanagerd (SwiftPM swift test works), and the host has no git-lfs.

Releasing

To publish Do this
A snapshot Merge to develop.
A release candidate git push origin develop:release/X.Y (first RC), then push or merge fixes to release/X.Y. Every push publishes the next X.Y.0-RC.N.
A release Merge release/X.Y to main and develop, then git tag vX.Y.0 && git push origin vX.Y.0.
A hotfix Push to hotfix/<name>; each push publishes patch + 1. Merge back to main and develop.

A push to main never publishes on its own. The tag is the release.

Changing this workflow

Repositories pin @v1, a tag that only moves after a change is proven. QRun-IO/qbit-sftp-data-integration is the canary and calls @main.

  1. Push the change to main.
  2. Prove it on the canary: push a branch there (verify), then re-run its latest develop run (publish), and any other path the change touches.
  3. Move the tag: git tag -fa v1 -m "<why>" <commit> && git push -f origin v1.

A breaking change (new required input, changed behavior for existing repos) gets v2 and a rollout, not a moved v1.

Differences from the orb

  • Publishes to the Gitea registry, not Maven Central, and does not GPG-sign.
  • main only verifies; the vX.Y.Z tag publishes the release, so a version is never published twice.
  • The release:rc PR label flow is gone. Cut a release candidate by pushing release/X.Y.
  • RC and hotfix publishes create a v<version> git tag. Maven builds create no GitHub or Gitea release objects; tags appear on the repository's Releases page. Node builds create a Gitea release only when release-files is set.
  • Not ported yet: security scans (gitleaks, Semgrep, OWASP, CycloneDX), SpotBugs/PMD, the QQQ middleware API-version check and frontend packaging.