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:
- The tag must equal
v+ theversion-fileversion. Set the version inpackage.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. - Install and
commandsrun as on any push; a failure stops the release. - Each
npm-publish-dirspackage goes to the owner's Gitea npm registry,https://git.kof22.com/api/packages/<owner>/npm/, with thePACKAGES_TOKENorg secret (secrets: inherit). A prerelease version gets dist-tagnext, a final versionlatest. The package keeps its name and scope; apublishConfig.registry(npmjs, GitHub) is ignored for the publish. A version that already exists fails: publish a new version instead. release-filesbecome a Gitea release for the tag (a prerelease for-<pre>versions) with a freshSHA256SUMS, and theCHANGELOG.mdsection whose##heading names the version as its notes. ASHA256SUMSamong 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
pushonly. 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
.ps1that reports failure withexit 1must run aspwsh -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 = $falsefirst. - 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'sSHA256SUMS. - 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 testcannot reachtestmanagerd(SwiftPMswift testworks), and the host has nogit-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.
- Push the change to
main. - Prove it on the canary: push a branch there (verify), then re-run its latest
developrun (publish), and any other path the change touches. - 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.
mainonly verifies; thevX.Y.Ztag publishes the release, so a version is never published twice.- The
release:rcPR label flow is gone. Cut a release candidate by pushingrelease/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 whenrelease-filesis set. - Not ported yet: security scans (gitleaks, Semgrep, OWASP, CycloneDX), SpotBugs/PMD, the QQQ middleware API-version check and frontend packaging.