Central library of reusable workflows and composite actions for Github Actions.
The reference for writing Actions workflows is available at the Github docs.
Most projects should be able to use these for their CI/CD pipelines.
To use a reusable workflow, write ks-no/github-actions-public/.github/workflows/<workflow-name>.yml@main under a
jobs.<job-name>.uses key in your workflow file. See the template files in ./caller-templates/ for examples of how to call these workflows.
If your repo demands that all Actions workflow calls are pinned to a commit SHA, replace "main" with the full, latest commit SHA of this repo.
java-service-build(for deployable services)java-library-internal-build(for private libraries)java-library-maven-central-build(for public libraries)
dotnet-library-build
nodejs-service-build
docker-image-build
deploy(to deploy a service with Helm; the Helm-chart must already have been populated with values and uploaded to Artifactory)open-api-specs(to publish OpenAPI spec files for spec-first projects)license-check(to check project licenses against Dependency-Track)dependency-management-sbom(to generate and publish a CycloneDX SBOM covering a Maven project's entire<dependencyManagement>, for parent POMs)zizmor-audit(to lint workflow files)
There are a large number of composite actions which can be used in custom workflows.
To use a composite action, write ks-no/github-actions-public/<workflow-name>@main under a
jobs.<job-name>.steps.[step].uses key in your workflow file.
The same caveat about commit SHA's as above applies here.
Some composites are simply wrappers around widely used third party actions, to avoid the hassle of updating commit SHA's. These wrappers are:
checkout(foractions/checkout)upload-artifact(foractions/upload-artifact)download-artifact(foractions/download-artifact)setup-java(foractions/setup-java)
More may be added in the future.
Snapshot repositories in Artifactory only keep the last few versions of each artifact. When a service
accumulates many Dependabot PRs between two main builds, those builds push out the image that is still
running in the cluster, and nodes that restart end up in ImagePullBackoff. This mirrors the
when { not { branch pattern: "^dependabot.*" } } guard used throughout fiks-buildlib.
Add the action as an early step in the job:
- name: Resolve publish policy
id: publish_policy
uses: ks-no/github-actions-public/resolve-publish-policy@mainIt exports PUBLISH_ARTIFACTS ("true"/"false") to $GITHUB_ENV for the rest of the job, and exposes the
same value as the step output publish for use across jobs via needs.<job>.outputs. Guard every step that
writes to Artifactory with if: env.PUBLISH_ARTIFACTS == 'true'.
Note that the branch is read from github.head_ref, falling back to github.ref_name. On pull_request
events github.ref is refs/pull/<n>/merge, so matching against refs/heads/dependabot/... never fires —
and since the caller templates only build main on push, Dependabot always arrives via pull_request.
All reusable workflows in this repository use the action. Set publish_from_dependabot: 'true' to opt out.
The JVM builds share one caching scheme, implemented in build-java-and-test, public-java-maven and the
two java-library-* workflows. Three rules keep ~/.m2/repository from growing without bound:
The directory is wiped before restore. On self-hosted runners ~/.m2/repository survives between jobs,
and between repositories that share a runner pool. Without rm -rf ~/.m2/repository, the cache save step
stores that whole accumulated directory as this repository's cache — which is why every service used to end
up at roughly the same size regardless of its own dependencies.
The cache is only saved from the default branch. A cache created in a pull request is scoped to that PR's own ref and cannot be read by any other PR, so saving from PRs only consumes the repository's 10 GB quota and evicts the main-branch cache that every build actually restores from. Builds on PRs still restore.
The key rotates monthly. restore-keys fetches the previous cache, the build downloads whatever versions
Dependabot just bumped, and the save writes old plus new — old versions are never removed, because nothing
knows they are dead. A $(date -u +%Y-%m) component in the key bounds that accumulation to one month. The
cost is one cold build per repository per month.
hashFiles('**/pom.xml') is evaluated once in a Resolve Maven cache key step so that restore and save use
the same key even when the build rewrites pom.xml (as release builds do).
Because the save is now an explicit step rather than a post-step, a failed build no longer refreshes the
cache. The dependency-management-sbom workflow keeps its own separate local repository and key prefix, and
only gets the monthly rotation — it never runs from a pull request.
Signs .nupkg files with a DigiCert KeyLocker code signing certificate and re-uploads them as a new artifact.
It must run on a windows-* runner. The reusable dotnet-library-build workflow already uses it for release builds;
custom workflows can call it directly.
Configuration it expects (all at organization level today):
- Variables:
SM_HOST,SM_KEY_PAIR_ALIAS - Secrets:
SM_API_KEY,SM_CLIENT_CERT_FILE(base64 of the service user's.p12),SM_CLIENT_CERT_PASSWORD
The action installs the KeyLocker tools, runs smctl windows certsync for the keypair alias, derives the certificate's
SHA-256 fingerprint from the Windows certificate store, signs with nuget sign through the DigiCert KSP and verifies
the result with nuget verify before uploading. It never uses smctl sign --simple: DigiCert does not list .nupkg
as supported for simple signing, and the signatures it produced had a non-canonical DER encoding that nuget.org's
repository countersigning broke (see NuGet/NuGetGallery#10942).
KS Digital utilizes both Github's runners (for public repos), and self-hosted runners (for private repos). All runners are Linux-based.
To use a Github-hosted runner in a custom workflow, write the label ubuntu-latest under a jobs.<job-name>.runs-on key;
for self-hosted runners, the label is [self-hosted, x64]. In reusable workflows, the runner type is already specified.
Some services may require more memory in order to be built. The label x64-large-memory is used to request a large runner.
This is also accomplished by filling in big-runner: true under a workflow dispatch to java-service-build.
Spec files in specs_folder can be JSON (.json) or YAML (.yaml, .yml). They are always bundled and published as
JSON, so foo.yaml is published as foo.json. Two specs with the same name and different extensions make the workflow fail.
Some spec files are placed in public repos. If this is the case, fill in is_public: true under a workflow dispatch to open-api-specs.
Request a test runner by specifying the label [self-hosted, test] in a custom workflow,
or @test-runner in a call to a reusable workflow.