Actions for Forgejo runners because apparently they just cannot handle GitHub ones and I hate finding them
  • TypeScript 92.9%
  • Shell 3.4%
  • JavaScript 3.2%
  • Python 0.5%
Find a file
forgejo-bot 42e0f57667 chore: rebuild action bundles
[skip ci]
2026-06-26 17:12:04 +00:00
.forgejo/workflows Move blazen base images 2026-04-20 07:37:02 -07:00
ai-review chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
appstore-metadata-sync fix(upload-appstore, appstore-metadata-sync): honor RUNNER_USER_HOME 2026-05-26 20:47:50 -07:00
appstore-testflight Add appstore-testflight action 2026-05-24 11:51:19 -07:00
cargo-audit Swap to Public org 2026-03-03 13:20:35 -08:00
cargo-get Add action to build actions lmao 2026-04-14 14:41:24 -07:00
cargo-publish-workspace fix(cargo-publish-workspace): parse crates.io 429 deadline for precise wait 2026-06-07 22:12:04 -07:00
check-appwrite-storage fix go setup 2026-05-26 10:30:11 -07:00
check-orphan-versions Use tmp branches for version detection and flagging rather than tags 2026-04-26 08:59:07 -07:00
ci-step-status ci-step-status + detect-changes: support diff-driven test skip 2026-05-28 13:09:59 -07:00
detect-changes ci-step-status + detect-changes: support diff-driven test skip 2026-05-28 13:09:59 -07:00
detect-markers feat(detect-markers): shared commit-marker parser for marker-driven CI/release 2026-06-13 12:18:35 -07:00
dispatch-forgejo-workflow Add dispatch-forgejo-workflow action 2026-05-24 11:17:00 -07:00
download-artifact fix(download-artifact): single-name extracts to <path>/<name>/, not <path>/ 2026-06-23 16:37:29 -07:00
download-file chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
download-resumable feat(download-resumable): composite curl download with stall detection + byte-offset resume 2026-06-12 16:55:43 -07:00
examples Add canonical release-mobile.yml example + index new mobile actions 2026-05-24 12:58:55 -07:00
github_format Fix tempdir on windows 2026-03-09 09:56:55 -07:00
golangci-lint chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
google-play-metadata-sync Add google-play-metadata-sync action 2026-05-24 12:53:06 -07:00
google-play-staged-rollout Add google-play-staged-rollout action 2026-05-24 12:53:06 -07:00
helm-unittest helm-unittest: migrate to node20 bundle 2026-04-15 15:08:33 -07:00
install-git-bash chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
intellitester chore: sync intellitester action.yml from Intellitester@c00669666ef0082f6f34f48379194a5d566f45c8 2026-05-11 18:55:46 +00:00
komodo-redeploy komodo-redeploy: composite action that fires per-stack Komodo deploy webhook 2026-05-28 15:19:15 -07:00
mint-appwrite-token mint-appwrite-token: mint per-file storage tokens via Appwrite Tokens API 2026-05-29 00:26:20 -07:00
prune-appwrite-storage fix go setup 2026-05-26 10:30:11 -07:00
pypi-publish-workspace Fix python urls 2026-04-23 10:43:33 -07:00
release-service release-service: migrate to node20 bundle 2026-04-15 14:55:25 -07:00
resolve-version fix(ci-actions): token TTL, resilient publish verify, manifest version seed 2026-06-05 13:44:33 -07:00
s3-cache chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-android-sdk chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-binaryen chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-bun chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-ccache chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-container-runtime Add setup-container-runtime (composite) and verify-artifact (node20) actions 2026-05-15 17:38:17 -07:00
setup-cuda-toolkit Add setup-cuda-toolkit action 2026-05-26 18:08:10 -07:00
setup-deno chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-docker chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-dotnet chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-fastlane chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-flutter chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-go chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-gradle chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-helm chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-java chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-kind chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-kotlin chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-kubectl chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-libclang chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-msvc chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-ninja chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-node chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-php feat(setup-php): composite action installing PHP CLI + Composer (Debian/Ubuntu) 2026-06-04 13:57:26 -07:00
setup-pnpm Add setup-pnpm 2026-05-11 13:45:21 -07:00
setup-podman chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-python chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-ruby chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-rust chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-sccache chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-shell chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-swift chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-system-deps chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-tauri-cli chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-terraform chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-uniffi-bindgen-go Fix bc ai is dum 2026-05-20 23:02:07 -07:00
setup-wasi-sdk chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-zig chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
setup-ztus chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
shared fix(platform/aptInstall): auto-recover from held-back transitive deps 2026-06-09 14:05:10 -07:00
sonarqube chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
submit-appstore-review Add submit-appstore-review action 2026-05-24 11:51:19 -07:00
tauri-build-android chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
tauri-build-ios chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
trivy chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
tus-upload chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
upload-appstore chore: rebuild action bundles 2026-06-26 17:12:04 +00:00
upload-appwrite-storage Fix Queries for upload 2026-04-15 08:58:36 -07:00
upload-artifact fix(upload-artifact): flatten file to basename in archive (drop --parents) 2026-06-10 19:18:08 -07:00
upload-firebase-app-distribution Add upload-firebase-app-distribution action 2026-05-24 11:17:00 -07:00
upload-google-play upload-google-play: add staged rollout, release notes, mapping file 2026-05-24 12:53:06 -07:00
verify-artifact verify-artifact: probe with GET+Range, not HEAD (Forgejo returns 405) 2026-05-18 19:11:56 -07:00
wrangler-deploy wrangler-deploy: composite action wrapping wrangler CLI 2026-05-31 18:44:46 -07:00
zlayer-build-push fix(zlayer build action): resolve latest ZLayer release dynamically 2026-06-07 17:46:23 -07:00
zregistry-check feat(zregistry): add composite actions (setup/publish/download/grant/revoke/check) 2026-06-04 08:51:31 -07:00
zregistry-download feat(zregistry): add composite actions (setup/publish/download/grant/revoke/check) 2026-06-04 08:51:31 -07:00
zregistry-grant feat(zregistry): add composite actions (setup/publish/download/grant/revoke/check) 2026-06-04 08:51:31 -07:00
zregistry-publish feat(zregistry): add composite actions (setup/publish/download/grant/revoke/check) 2026-06-04 08:51:31 -07:00
zregistry-revoke feat(zregistry): add composite actions (setup/publish/download/grant/revoke/check) 2026-06-04 08:51:31 -07:00
zregistry-setup feat(zregistry): add composite actions (setup/publish/download/grant/revoke/check) 2026-06-04 08:51:31 -07:00
zregistry-token fix(zregistry-token): default scope to packages.publish so minted tokens carry the write scope ZRegistry gates on (empty no longer mints unscoped); also keyless token-exchange mode 2026-06-06 18:50:40 -07:00
zrelay-notify Add zrelay-notify action 2026-05-24 11:17:46 -07:00
.gitattributes Add action to build actions lmao 2026-04-14 14:41:24 -07:00
.gitignore Add action to build actions lmao 2026-04-14 14:41:24 -07:00
bun.lock setup-binaryen: install wasm-opt/binaryen from GitHub releases 2026-05-27 16:43:51 -07:00
LICENSE Initial commit 2025-11-30 21:31:00 +00:00
package.json setup-binaryen: install wasm-opt/binaryen from GitHub releases 2026-05-27 16:43:51 -07:00
README.md feat(detect-markers): shared commit-marker parser for marker-driven CI/release 2026-06-13 12:18:35 -07:00
tsconfig.base.json Add action to build actions lmao 2026-04-14 14:41:24 -07:00

actions

Actions for Forgejo runners because apparently they just cannot handle GitHub ones and I hate finding them

Language Setup Actions

setup-go

Install Go from official releases.

- uses: https://forge.blackleafdigital.com/Public/actions/setup-go@main
  with:
    go-version: '1.23'    # optional, default: 1.23
    cache: true           # optional, default: true

setup-node

Install Node.js with package manager support.

- uses: https://forge.blackleafdigital.com/Public/actions/setup-node@main
  with:
    node-version: '22'           # optional, default: 22
    package-manager: pnpm        # optional: npm, yarn, pnpm (default: npm)
    cache: true                  # optional, default: true

setup-python

Install Python via uv (fast Python package manager).

- uses: https://forge.blackleafdigital.com/Public/actions/setup-python@main
  with:
    python-version: '3.12'    # optional, default: 3.12
    uv-version: latest        # optional, default: latest
    cache: true               # optional, default: true

setup-rust

Install Rust toolchain via rustup with optional sccache integration.

- uses: https://forge.blackleafdigital.com/Public/actions/setup-rust@main
  with:
    toolchain: stable              # optional, default: stable
    components: rustfmt, clippy    # optional
    targets: x86_64-unknown-linux-musl  # optional

With sccache + S3

- uses: https://forge.blackleafdigital.com/Public/actions/setup-rust@main
  with:
    components: rustfmt, clippy
    sccache: true
    sccache-s3-bucket: ${{ secrets.S3_BUCKET }}
    sccache-s3-endpoint: https://${{ secrets.S3_ENDPOINT }}
    sccache-s3-region: ${{ secrets.S3_REGION }}
    sccache-s3-access-key-id: ${{ secrets.S3_ACCESS_KEY }}
    sccache-s3-secret-access-key: ${{ secrets.S3_ACCESS_SECRET }}
    sccache-key-prefix: sccache/myproject

setup-bun

Install Bun JavaScript runtime.

- uses: https://forge.blackleafdigital.com/Public/actions/setup-bun@main
  with:
    bun-version: latest    # optional, default: latest

setup-deno

Install Deno JavaScript/TypeScript runtime.

- uses: https://forge.blackleafdigital.com/Public/actions/setup-deno@main
  with:
    deno-version: latest    # optional, default: latest

setup-java

Install Java/JDK via Eclipse Temurin (Adoptium).

- uses: https://forge.blackleafdigital.com/Public/actions/setup-java@main
  with:
    java-version: '21'        # optional, default: 21
    distribution: temurin     # optional, default: temurin

setup-dotnet

Install .NET SDK via official Microsoft installer.

- uses: https://forge.blackleafdigital.com/Public/actions/setup-dotnet@main
  with:
    dotnet-version: '8.0'    # optional, default: 8.0

setup-zig

Install Zig compiler.

- uses: https://forge.blackleafdigital.com/Public/actions/setup-zig@main
  with:
    zig-version: '0.13.0'    # optional, default: 0.13.0 (or 'master')

setup-swift

Install Swift toolchain.

- uses: https://forge.blackleafdigital.com/Public/actions/setup-swift@main
  with:
    swift-version: '5.10'    # optional, default: 5.10

Tool Setup Actions

setup-kind

Install Kind (Kubernetes in Docker) with optional cluster creation.

- uses: https://forge.blackleafdigital.com/Public/actions/setup-kind@main
  with:
    kind-version: v0.24.0     # optional, default: v0.24.0
    cluster-name: kind        # optional, default: kind
    wait: '120s'              # optional, default: 120s
    create-cluster: true      # optional, default: true

setup-kubectl

Install kubectl CLI.

- uses: https://forge.blackleafdigital.com/Public/actions/setup-kubectl@main
  with:
    kubectl-version: stable    # optional, default: stable (latest)

setup-helm

Install Helm package manager for Kubernetes.

- uses: https://forge.blackleafdigital.com/Public/actions/setup-helm@main
  with:
    helm-version: latest    # optional, default: latest

setup-terraform

Install Terraform from HashiCorp.

- uses: https://forge.blackleafdigital.com/Public/actions/setup-terraform@main
  with:
    terraform-version: latest    # optional, default: latest

setup-docker

Install Docker (Linux only, macOS requires Docker Desktop).

- uses: https://forge.blackleafdigital.com/Public/actions/setup-docker@main
  with:
    docker-version: latest    # optional, default: latest

setup-podman

Install Podman container engine.

- uses: https://forge.blackleafdigital.com/Public/actions/setup-podman@main
  with:
    podman-version: latest    # optional, default: latest (system package)

setup-sccache

Standalone sccache setup with optional S3 backend.

- uses: https://forge.blackleafdigital.com/Public/actions/setup-sccache@main
  with:
    version: v0.8.1                # optional
    bucket: ${{ secrets.S3_BUCKET }}
    endpoint: https://${{ secrets.S3_ENDPOINT }}
    region: ${{ secrets.S3_REGION }}
    access-key: ${{ secrets.S3_ACCESS_KEY }}
    secret-key: ${{ secrets.S3_ACCESS_SECRET }}
    key-prefix: sccache/myproject

Security & Quality Actions

cargo-get

Extract metadata (version, name, description, authors, etc.) from Cargo.toml without needing Rust installed. Handles workspace inheritance ({ workspace = true } fields).

- uses: https://forge.blackleafdigital.com/Public/actions/cargo-get@main
  with:
    path: .                    # optional, default: .  (dir or Cargo.toml path)
    workspace-member: mycrate  # optional: specific workspace member to query

Outputs include package_name, package_version, package_version_{major,minor,patch,pre,build}, package_edition, package_rust_version, package_authors, package_description, package_license, package_publish, workspace_members, is_workspace, and more.

cargo-publish-workspace

Idempotent, rate-limit-aware publisher for Rust workspaces. Skips crates already live at the target version on crates.io and retries each crate with per-crate exponential backoff (up to 5 minutes) on HTTP 429. Publishes in topological dependency order.

- uses: https://forge.blackleafdigital.com/Public/actions/cargo-publish-workspace@main
  with:
    version: ${{ needs.version.outputs.version }}
    registry: crates-io                                   # optional, default: crates-io
    exclude: blazen-py,blazen-node                        # optional: comma-separated skip list
    cargo-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
    max-backoff-seconds: '300'                            # optional, default: 300
    max-attempts-per-crate: '5'                           # optional, default: 5

Requires setup-rust to have run earlier in the job. Outputs published, skipped, total (comma-separated lists + count).

cargo-audit

Run security audit for Rust dependencies.

- uses: https://forge.blackleafdigital.com/Public/actions/cargo-audit@main
  with:
    deny: unmaintained  # optional: fail on unmaintained crates

trivy

Run Trivy security scanner for vulnerabilities in code, containers, and dependencies.

- uses: https://forge.blackleafdigital.com/Public/actions/trivy@main
  with:
    scan-type: fs                    # optional: fs, image, repo (default: fs)
    severity: CRITICAL,HIGH          # optional (default: CRITICAL,HIGH)
    exit-code: '1'                   # optional: fail on findings (default: 1)
    ignore-unfixed: false            # optional: ignore unpatched vulns
    format: table                    # optional: table, json, sarif

Scan Docker image

- uses: https://forge.blackleafdigital.com/Public/actions/trivy@main
  with:
    scan-type: image
    image-ref: myapp:latest
    severity: CRITICAL

sonarqube

Run SonarQube code quality analysis.

- uses: https://forge.blackleafdigital.com/Public/actions/sonarqube@main
  with:
    host-url: ${{ secrets.SONAR_HOST_URL }}
    token: ${{ secrets.SONAR_TOKEN }}
    project-key: my-project
    project-name: My Project         # optional (defaults to project-key)
    sources: src                     # optional (default: .)
    exclusions: '**/*.test.ts'       # optional: patterns to exclude

ai-review

AI-powered code review using Z.AI (GLM) or other OpenAI-compatible providers.

- uses: https://forge.blackleafdigital.com/Public/actions/ai-review@main
  with:
    api-key: ${{ secrets.ZAI_API_KEY }}
    forgejo-token: ${{ secrets.FORGEJO_TOKEN }}
    forgejo-url: https://forge.blackleafdigital.com
    model: glm-4.6                   # optional (default: glm-4.6)
    base-url: https://api.z.ai/v1    # optional (default: Z.AI)
    provider: openai                 # optional: openai, anthropic, ollama

With OpenAI

- uses: https://forge.blackleafdigital.com/Public/actions/ai-review@main
  with:
    api-key: ${{ secrets.OPENAI_API_KEY }}
    forgejo-token: ${{ secrets.FORGEJO_TOKEN }}
    forgejo-url: https://forge.blackleafdigital.com
    base-url: https://api.openai.com/v1
    model: gpt-4o

Mobile App Release

End-to-end Tauri 2.x mobile release pipeline — toolchain setup, signed builds, store uploads, TestFlight / staged rollouts, metadata sync, and App Review submission. See examples/release-mobile.yml for the canonical workflow that chains these actions together (parallel iOS + Android jobs with ci-step-status gating, zrelay-notify on success/failure, and a Forgejo release at the end).

setup-fastlane

Install fastlane (and bundler) onto the runner. Requires Ruby on PATH (run setup-ruby first). Detects a Gemfile and falls back to gem install fastlane when none is present.

- uses: https://forge.blackleafdigital.com/Public/actions/setup-ruby@main
  with:
    ruby-version: '3.3'
- uses: https://forge.blackleafdigital.com/Public/actions/setup-fastlane@main
  with:
    fastlane-version: latest    # optional, default: latest
    bundler-version: latest     # optional, default: latest
    gemfile-dir: .              # optional, default: .

setup-tauri-cli

Install the Tauri 2.x CLI via cargo-binstall (preferred, prebuilt) or cargo install, with optional iOS / Android Rust target installation.

- uses: https://forge.blackleafdigital.com/Public/actions/setup-tauri-cli@main
  with:
    tauri-version: latest    # optional, default: latest
    target: ios              # optional: desktop | ios | android | all
    use-prebuilt: true       # optional, default: true (prefer cargo-binstall)

tauri-build-ios

Build a Tauri 2.x iOS app and produce a signed IPA ready for App Store / TestFlight / ad-hoc distribution. Outputs ipa-path, bundle-version, and bundle-short-version.

- uses: https://forge.blackleafdigital.com/Public/actions/tauri-build-ios@main
  with:
    bundle-id: com.blackleaf.imaige
    code-sign-identity: ${{ secrets.IOS_CODE_SIGN_IDENTITY }}
    provisioning-profile-name: ${{ secrets.IOS_PROVISIONING_PROFILE_NAME }}
    team-id: ${{ secrets.IOS_TEAM_ID }}
    target: aarch64-apple-ios    # optional, default: aarch64-apple-ios
    export-method: app-store     # optional: app-store | ad-hoc | enterprise | development

tauri-build-android

Build a signed Android AAB (and optional APK) from a Tauri 2.x project using the Tauri CLI + Gradle. Decodes a base64 keystore on the fly. Outputs aab-path, apk-path, version-code, version-name.

- uses: https://forge.blackleafdigital.com/Public/actions/tauri-build-android@main
  with:
    package-name: com.blackleaf.imaige
    keystore-base64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
    keystore-password: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
    key-alias: ${{ secrets.ANDROID_KEY_ALIAS }}
    key-password: ${{ secrets.ANDROID_KEY_PASSWORD }}
    targets: aarch64-linux-android    # optional, CSV of Android Rust targets
    build-apk: false                  # optional: also produce a signed APK

upload-appstore

Upload an IPA to App Store Connect (TestFlight) using API-key authentication. With wait-for-processing: true it polls App Store Connect until the build reaches VALID (or fails) and exposes build-id + processing-state for downstream TestFlight / review steps. Always emits bundle-version and bundle-short-version parsed from the IPA.

- uses: https://forge.blackleafdigital.com/Public/actions/upload-appstore@main
  with:
    ipa-path: ${{ steps.build-ios.outputs.ipa-path }}
    api-key-id: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
    issuer-id: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
    api-private-key: ${{ secrets.APP_STORE_CONNECT_API_PRIVATE_KEY }}
    wait-for-processing: 'true'    # optional, default: false
    timeout-minutes: '60'          # optional, default: 60
    poll-interval-seconds: '30'    # optional, default: 30

appstore-testflight

Attach a previously-uploaded build to TestFlight beta groups, set what-to-test localizations, toggle auto-notify, and optionally submit for external beta review.

- uses: https://forge.blackleafdigital.com/Public/actions/appstore-testflight@main
  with:
    api-key-id: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
    issuer-id: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
    api-private-key: ${{ secrets.APP_STORE_CONNECT_API_PRIVATE_KEY }}
    bundle-id: com.blackleaf.imaige
    build-version: ${{ steps.build-ios.outputs.bundle-version }}
    beta-groups: 'Internal QA,External Beta'
    submit-for-beta-review: auto    # optional: true | false | auto

appstore-metadata-sync

Sync App Store Connect metadata, screenshots, and app preview videos via fastlane deliver. Requires setup-fastlane to have run earlier in the job. Reads the standard fastlane/metadata + fastlane/screenshots directory layout.

- uses: https://forge.blackleafdigital.com/Public/actions/appstore-metadata-sync@main
  with:
    api-key-id: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
    issuer-id: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
    api-private-key: ${{ secrets.APP_STORE_CONNECT_API_PRIVATE_KEY }}
    app-identifier: com.blackleaf.imaige
    version-string: 1.2.3            # optional: bump to / target this version
    skip-screenshots: false          # optional, default: false
    skip-metadata: false             # optional, default: false

submit-appstore-review

Submit an App Store Connect version for review via the App Store Connect API. Supports auto-release on approval, phased rollout, and per-locale "what's new" text.

- uses: https://forge.blackleafdigital.com/Public/actions/submit-appstore-review@main
  with:
    api-key-id: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
    issuer-id: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
    api-private-key: ${{ secrets.APP_STORE_CONNECT_API_PRIVATE_KEY }}
    bundle-id: com.blackleaf.imaige
    version-string: 1.2.3
    build-version: ${{ steps.build-ios.outputs.bundle-version }}
    auto-release: 'true'    # optional, default: true (AFTER_APPROVAL)
    phased-release: 'false' # optional, default: false (7-day phased rollout)

upload-google-play

Upload an AAB to Google Play via the Developer API using a service account. Supports release-track selection, staged rollout with a user-fraction, per-locale release notes (JSON), and optional ProGuard/R8 mapping file upload after the bundle upload.

- uses: https://forge.blackleafdigital.com/Public/actions/upload-google-play@main
  with:
    aab-path: ${{ steps.build-android.outputs.aab-path }}
    package-name: com.blackleaf.imaige
    service-account-json: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}
    track: internal               # optional: internal | alpha | beta | production
    user-fraction: '0.10'         # optional: start a staged rollout at 10%
    release-notes-json: '[{"language":"en-US","text":"Bug fixes."}]'  # optional
    mapping-file: app/build/outputs/mapping/release/mapping.txt        # optional

google-play-staged-rollout

Adjust an existing Google Play release's staged-rollout userFraction and status (set / halt / resume / complete) without re-uploading the bundle. Useful for promoting an internal upload to a wider rollout or pausing a rollout that's misbehaving.

- uses: https://forge.blackleafdigital.com/Public/actions/google-play-staged-rollout@main
  with:
    service-account-json: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}
    package-name: com.blackleaf.imaige
    track: production
    action: set            # set | halt | resume | complete
    user-fraction: '0.10'  # required for set / resume

google-play-metadata-sync

Sync per-locale Google Play store listings (title, short / full description, video URL) and image assets (icon, feature graphic, phone / tablet / TV / wear screenshots) via the Play Developer API v3. Reads the fastlane/metadata/android/<locale>/... directory layout.

- uses: https://forge.blackleafdigital.com/Public/actions/google-play-metadata-sync@main
  with:
    service-account-json: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}
    package-name: com.blackleaf.imaige
    listings-dir: ./fastlane/metadata/android    # optional, default shown
    image-types: 'phoneScreenshots,featureGraphic,icon'  # optional

zrelay-notify

POST a notification to the BlackLeafDigital ZRelay notification relay. Supports SMS, email, Discord, iMessage, FCM push, web push, and APNs channels. Handy as the final step of a release pipeline to ping the team on success / failure.

- uses: https://forge.blackleafdigital.com/Public/actions/zrelay-notify@main
  with:
    api-key: ${{ secrets.ZRELAY_API_KEY }}
    channel: email                       # sms | email | discord | imessage | push | webpush | apns
    recipient: ${{ secrets.ZRELAY_NOTIFY_RECIPIENT }}
    subject: "Release v${{ inputs.version }} shipped"
    body: |
      iOS + Android builds uploaded successfully.
    fail-on-error: 'true'    # optional, default: true

CI / Release Orchestration

detect-markers

Parse commit-message markers into job-gating outputs. Single source of truth for the house marker convention — replaces the inline contains(...)/bash that every repo used to duplicate.

- id: m
  uses: https://forge.blackleafdigital.com/Public/actions/detect-markers@main
  with:
    message: ${{ github.event.head_commit.message }}   # push event
    # message: ${{ github.event.workflow_run.head_commit.message }}  # workflow_run
    # default-bump: patch                               # optional
Marker Output Meaning
[release] (or [release minor] / [release:major]) release=true opt into the publish/release chain
[np] / [no-publish] no-publish=truerelease=false suppress publishing
[skip] / [skip-checks] / [skip-ci] / [skip ci] / [ci skip] skip=truerelease=false skip CI/checks
[fast] fast=true skip test/lint/audit jobs; release still fires
[purge] purge=true force fresh rebuild / invalidate cached artifacts
[major] / [minor] / [patch] bump=<x> bump type (default patch)
[vX.Y.Z] (opt. -pre) version=X.Y.Z[-pre] explicit version override
push event + release dry-run=false live publish; otherwise true

Outputs: release, no-publish, skip, fast, purge, bump, version, dry-run.

Canonical marker-driven release pattern (push-triggered ci.yml → dispatch release.yml, since Forgejo's on: workflow_run hook fires unreliably):

  dispatch-release:
    runs-on: ubuntu-latest
    needs: [lint, test]            # gate on green CI
    if: success() && github.event_name == 'push' && github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - id: m
        uses: https://forge.blackleafdigital.com/Public/actions/detect-markers@main
        with:
          message: ${{ github.event.head_commit.message }}
      - if: steps.m.outputs.release == 'true'
        uses: https://forge.blackleafdigital.com/Public/actions/dispatch-forgejo-workflow@main
        with:
          workflow: release.yml
          ref: main
          token: ${{ secrets.FORGEJO_ACTIONS_TOKEN }}
          inputs-json: >-
            {"version":"${{ steps.m.outputs.version }}","bump":"${{ steps.m.outputs.bump }}","dry_run":"false"}

dispatch-forgejo-workflow

Fire a Forgejo workflow_dispatch via the REST API (reliable replacement for the flaky on: workflow_run cross-workflow trigger). Optionally polls for completion.

- uses: https://forge.blackleafdigital.com/Public/actions/dispatch-forgejo-workflow@main
  with:
    workflow: release.yml          # required — workflow filename
    ref: main                      # optional, default: main
    inputs-json: '{"dry_run":"false"}'   # optional JSON of inputs
    token: ${{ secrets.FORGEJO_ACTIONS_TOKEN }}   # required
    wait: 'false'                  # optional — poll until run completes

ZRegistry Actions

Publish, install, and license-manage packages on a self-hosted ZRegistry. The setup/publish/grant/ revoke/check actions wrap the prebuilt zregistry admin CLI; zregistry-download fetches artifacts over each ecosystem's native HTTP route (no CLI download subcommand exists). ZRegistry is a private repo, so the CLI-installing actions need a Forgejo token (forgejo-token) to fetch the release asset.

Release-asset dependency: zregistry-setup downloads zregistry-{target}.tar.gz from a ZRegistry release, where the CLI binary inside is named zregistry-cli-{target}. The ZRegistry CI/release job currently builds these tarballs (x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu) and uploads them as CI artifacts — the release pipeline must also attach them as release assets for these actions to resolve them via the releases API.

zregistry-setup

Download the prebuilt zregistry CLI from a ZRegistry release and add it to PATH. Resolves latest (or a pinned tag) via the Forgejo releases API and selects the asset for the runner arch (x86_64 / aarch64).

- uses: https://forge.blackleafdigital.com/Public/actions/zregistry-setup@main
  with:
    version: latest                              # optional, default: latest (or a tag like v1.2.3)
    forgejo-url: https://forge.blackleafdigital.com   # optional
    repo: BlackLeafDigital/ZRegistry             # optional
    token: ${{ secrets.FORGEJO_TOKEN }}          # required: ZRegistry is private
# outputs: cli-path, resolved-tag

zregistry-publish

Install the CLI and zregistry publish an artifact. Maps inputs → flags; the publish token travels via ZREGISTRY_TOKEN.

- uses: https://forge.blackleafdigital.com/Public/actions/zregistry-publish@main
  with:
    ecosystem: npm                               # required
    file: ./dist/core-1.2.3.tgz                  # required
    name: '@ztana/core'                          # required for npm + generic
    version: '1.2.3'                             # required for generic
    registry-url: https://registry.blackleafdigital.com   # optional
    token: ${{ secrets.ZREGISTRY_TOKEN }}        # required (→ ZREGISTRY_TOKEN)
    forgejo-token: ${{ secrets.FORGEJO_TOKEN }}  # to fetch the private CLI asset
# outputs: status, output

zregistry-download

Fetch a package artifact via the ecosystem-native HTTP route (npm tarball, pypi file, cargo …/download, or generic /generic/{name}/{version}/{filename}). The token is sent in each ecosystem's native auth slot — npm → Bearer, pypi/cargo/ generic → HTTP Basic __token__:<token> (per ZRegistry/docs/installing.md).

- uses: https://forge.blackleafdigital.com/Public/actions/zregistry-download@main
  with:
    ecosystem: generic                           # optional, default: generic
    package: my-app                              # required
    version: '1.0.0'                             # default: latest (npm only resolves latest)
    filename: my-app-1.0.0.bin                   # required for generic route
    output-dir: ./downloads                      # optional, default: .
    registry-url: https://registry.blackleafdigital.com   # optional
    token: ${{ secrets.ZREGISTRY_TOKEN }}        # license key / JWT for non-public packages
# outputs: artifact-path, http-status

zregistry-grant

Grant package access via ZBilling — a SKU bundle (mode: skugrant-sku) or a per-license grant (mode: licensegrant-license).

- uses: https://forge.blackleafdigital.com/Public/actions/zregistry-grant@main
  with:
    mode: license                                # sku | license
    ecosystem: npm                               # required
    scope: '@ztana/*'                            # required (package glob)
    license: ${{ secrets.CUSTOMER_LICENSE }}     # required when mode=license
    # sku: pkg_zql_pro                           # required when mode=sku
    note: 'complimentary access'                 # optional (mode=license)
    zbilling-url: https://billing.blackleafdigital.com    # required
    zbilling-token: ${{ secrets.ZBILLING_ENTITLEMENTS_TOKEN }}   # required
    forgejo-token: ${{ secrets.FORGEJO_TOKEN }}  # to fetch the private CLI asset
# outputs: output

zregistry-revoke

Revoke a ZBilling license (zregistry revoke --license [--reason]).

- uses: https://forge.blackleafdigital.com/Public/actions/zregistry-revoke@main
  with:
    license: ${{ secrets.CUSTOMER_LICENSE }}     # required
    reason: 'refund'                             # optional
    zbilling-url: https://billing.blackleafdigital.com    # required
    zbilling-token: ${{ secrets.ZBILLING_ENTITLEMENTS_TOKEN }}   # required
    forgejo-token: ${{ secrets.FORGEJO_TOKEN }}  # to fetch the private CLI asset
# outputs: output

zregistry-check

Check whether a license may install a package (zregistry check). Surfaces the {allowed, reason} decision as outputs; optionally fails the step when denied.

- uses: https://forge.blackleafdigital.com/Public/actions/zregistry-check@main
  with:
    license: ${{ secrets.CUSTOMER_LICENSE }}     # required
    ecosystem: npm                               # required
    package: '@ztana/core'                       # required
    zbilling-url: https://billing.blackleafdigital.com    # required
    zbilling-token: ${{ secrets.ZBILLING_ENTITLEMENTS_TOKEN }}   # required
    fail-on-denied: 'false'                      # optional, default: false
    forgejo-token: ${{ secrets.FORGEJO_TOKEN }}  # to fetch the private CLI asset
# outputs: decision (JSON), allowed (true|false), reason