No description
Find a file
2026-06-13 15:47:07 +00:00
actions feat: add setup-bitcrafts-apt composite action 2026-06-12 11:53:55 +01:00
AGENTS.md chore: rename CLAUDE.md to AGENTS.md for Cline 2026-06-13 16:23:28 +01:00
LICENSE chore: add Apache-2.0 LICENSE 2026-06-13 16:23:27 +01:00
README.md docs: secrets live at org level — user-account secrets never reach orgs 2026-06-12 15:50:44 +01:00

ci-actions

Shared Forgejo Actions composite actions for the BitCrafts ecosystem (bitcrafts first-party + external third-party orgs). Keeping them here (DRY) means every repo's release.yml calls the same audited, injection-safe publish/release logic instead of copy-pasting shell.

Two actions:

Action Purpose
actions/publish-deb Upload .deb package(s) to this instance's Debian registry (idempotent delete-then-upload).
actions/forgejo-release Create a notes-only Forgejo Release for the current tag (marker only — binaries live in the registry).

Using these actions (cross-repo)

Forgejo resolves cross-repo uses: by full URL. Reference an action by its absolute URL on this instance and pin the @v1 tag (this repo is public, so runners check it out anonymously):

- uses: https://git.bit-crafts.com/bitcrafts/ci-actions/actions/publish-deb@v1

Per-org model: publish-deb no longer defaults the registry owner — every caller must pass it explicitly (bitcrafts for first-party apps, external for repackaged third-party + libs). This prevents a repo from silently publishing into the wrong namespace. The auth user is likewise required (no hardcoded default) — pass ${{ secrets.PKG_USER }}.

Security model (all actions)

All user-supplied inputs are bound to environment variables in the step's env: block and referenced as "$VAR" inside run: — they are never interpolated into the shell body (no ${{ inputs.x }} directly in run:). This blocks shell/command injection through crafted input values. Both actions run with set -euo pipefail. forgejo-release builds its JSON payload with python3 -c 'json.dumps(...)' reading from os.environ, so the body cannot break out of the JSON. Endpoints are derived from ${{ github.server_url }} (the runner's own instance URL) — no hardcoded IPs.


actions/publish-deb

Publishes one or more .deb files to the Debian package registry of this Forgejo instance. For each matched file it DELETEs the existing (package, version, arch) (tolerating 404) then uploads it — so re-running a release overwrites rather than 409-conflicting.

Inputs

Input Required Default Description
token yes PAT with write:package scope (secrets.PKG_TOKEN_PACKAGES). The built-in github.token does not carry the package scope.
owner yes Registry owner/org: bitcrafts (apps) or external (third-party + libs).
user yes Auth username for the registry (pass secrets.PKG_USER).
path no dist/*.deb Glob of .deb files to publish.
distribution no trixie Debian distribution (apt dists/<distribution>).
component no main Debian component.

Fails the job if no file matches path. Prints a per-file delete/upload -> HTTP code summary.

Example

- name: Publish .deb to the bitcrafts registry
  uses: https://git.bit-crafts.com/bitcrafts/ci-actions/actions/publish-deb@v1
  with:
    token: ${{ secrets.PKG_TOKEN_PACKAGES }}
    owner: bitcrafts
    user:  ${{ secrets.PKG_USER }}
    path: "dist/*.deb"

Consumers (apt): the registry is served at https://pkgs.bit-crafts.com. With the per-org split, apps come from the bitcrafts owner and third-party/libs from external:

deb https://pkgs.bit-crafts.com/api/packages/bitcrafts/debian trixie main   # bitcrafts apps
deb https://pkgs.bit-crafts.com/api/packages/external/debian  trixie main   # caddy/forgejo/wg-easy/liburing/…

(key at .../debian/repository.key; one-line .list, not DEB822.)


actions/forgejo-release

Creates a notes-only Forgejo Release for the tag that triggered the run. It attaches no binary assets — the canonical artifacts are the .debs in the Debian registry; the Release object is just a human-facing marker/changelog. It is non-fatal: on a non-2xx/409 response it emits ::warning:: and the job still succeeds (the release is not load-bearing).

Inputs

Input Required Default Description
token yes Token with repo write. Pass the built-in ${{ github.token }}.
user yes Auth username (pass secrets.PKG_USER).
body no Automated release. Binaries are published to the Debian package registry. Release notes body.

Behavior by HTTP status: 2xx → created · 409 → already exists (ok) · anything else → ::warning:: (job continues).

Example

- name: Create Forgejo release marker
  uses: https://git.bit-crafts.com/bitcrafts/ci-actions/actions/forgejo-release@v1
  with:
    token: ${{ github.token }}
    user:  ${{ secrets.PKG_USER }}
    body: "Automated release — my-project (.deb). Binary is published to the Debian package registry."

actions/setup-bitcrafts-apt

Configures the per-owner bit-crafts Debian registries inside the job container (keyring in /etc/apt/keyrings/<owner>.asc, source in /etc/apt/sources.list.d/<owner>.list) and runs apt-get update. No secrets: the registries are anonymously readable. Replaces the inline copy-pasted apt blocks in toolkit/forge/bc-system-cooling.

Inputs

Input Required Default Notes
owners no bitcrafts external space-separated registry owners
host no pkgs.bit-crafts.com registry host
distribution no trixie Debian distribution
component no main registry component

Example

      - name: Configure bit-crafts apt registries
        uses: https://git.bit-crafts.com/bitcrafts/ci-actions/actions/setup-bitcrafts-apt@v1
        # or restrict: with: { owners: bitcrafts }

Combined release.yml (typical consumer)

name: release
on:
  push:
    tags: ["v*"]
concurrency:
  group: release-${{ github.ref }}
  cancel-in-progress: false        # serialize the delete-then-upload to the registry
jobs:
  package-publish:
    runs-on: build-large
    container: git.bit-crafts.com/bitcrafts/ci-base-c:trixie-vX.Y.Z   # versioned pin (never @sha256)
    steps:
      - uses: actions/checkout@v4
      - run: |
          set -euo pipefail
          bash packaging/build-deb.sh "${GITHUB_REF_NAME#v}"   # produces dist/*.deb
      - uses: https://git.bit-crafts.com/bitcrafts/ci-actions/actions/publish-deb@v1
        with:
          token: ${{ secrets.PKG_TOKEN_PACKAGES }}
          owner: bitcrafts
          user:  ${{ secrets.PKG_USER }}
          path: "dist/*.deb"
      - uses: https://git.bit-crafts.com/bitcrafts/ci-actions/actions/forgejo-release@v1
        with:
          token: ${{ github.token }}
          user:  ${{ secrets.PKG_USER }}

Required secrets

Set at org level so every repo inherits them — never in the user account's Actions secrets (those reach personal repos only, not orgs).

Secret Org Used by Scope
PKG_TOKEN_PACKAGES orgs bitcrafts + external publish-deb (all owners) single PAT write:package
PKG_USER both publish-deb, forgejo-release registry auth username
DEPLOY_KEY bitcrafts / unmanagedbytes site deploy workflows (not these actions) per-container rrsync SSH key
IMG_BUILD_KEY repo bitcrafts/incus-images golden-image workflows (not these actions) forced-command SSH key to the srv03 builder

The release token (github.token) is provided automatically per run — no secret needed for forgejo-release's token.

Versioning

Consumers pin @v1. Breaking changes to an action's inputs bump the major tag (v2); v1 is moved forward for backward-compatible fixes. Don't track @main from consumers.