|
|
||
|---|---|---|
| actions | ||
| AGENTS.md | ||
| LICENSE | ||
| README.md | ||
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-debno longer defaults the registryowner— every caller must pass it explicitly (bitcraftsfor first-party apps,externalfor repackaged third-party + libs). This prevents a repo from silently publishing into the wrong namespace. The authuseris 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.