Skip to content

Contributing

Prerequisites

  • Bun 1.3.x (the repo pins bun@1.3.13).
  • dprint for formatting (invoked via bun run fmt).
bash
bun install

Monorepo layout

Bun workspaces + Turborepo. Packages live under packages/*:

text
packages/
  core/             @actup/core         — engine, no host networking
  provider-github/  @actup/provider-github
  provider-gitlab/  @actup/provider-gitlab
  provider-gitea/   @actup/provider-gitea
  providers/        @actup/providers    — host→provider registry
  cli/              @actup/cli          — the `actup` binary

Each package exports from ./src/index.ts, uses Node subpath imports (#*./src/*, #pkg./package.json), and depends on siblings via workspace:*. @actup/core/src/index.ts re-exports the whole public surface (action-ref, cache, caching-provider, config, discovery, engine, error, http, parser, policy, provider, resolution, version).

Quality gates

Turbo runs each task across all packages.

bash
bun run typecheck   # turbo run typecheck  → tsc --noEmit per package
bun test            # turbo run test       → bun test per package
bun run fmt         # dprint fmt .

Run targeted tests during development from a package directory or with bun test <path>; prefer focused runs over the full suite.

Code standards (from CLAUDE.md)

  • No any, no non-null !, no as type assertions. The only sanctioned as is the Sha40 smart-constructor after its regex check.
  • Model domain with discriminated unions; parse inputs at boundaries.
  • No lint-suppression annotations (@ts-ignore, eslint-disable, etc.). Fix root causes.
  • Errors flow through the typed Result/ActupError model, not thrown control flow at boundaries.

How to add a provider

A provider is a host-metadata source implementing the Provider contract from @actup/core (packages/core/src/provider.ts):

ts
interface Provider {
	kind(): ProviderKind; // 'github' | 'gitlab' | 'gitea'
	host(): string;
	getVersions(repo: ActionRepoId): Promise<Result<VersionSet>>;
	resolveRefToSha(repo: ActionRepoId, ref: string): Promise<Result<Sha40>>;
	getDefaultBranch(repo: ActionRepoId): Promise<Result<string>>;
}

Steps:

  1. Scaffold a package under packages/provider-<host>/ mirroring an existing one (provider-github is the reference): package.json (@actup/provider-<host>, private, exports/imports maps, @actup/core + zod deps, build/typecheck/test scripts), tsconfig.json, src/index.ts, tests/index.test.ts. Add it to the workspace by virtue of packages/*.
  2. Implement the contract. Construct from ProviderOptions (host, optional apiBase, token, maxTagsPerRepo, apiMode). Do all network access through httpJson(schema, opts) from @actup/core — pass a zod schema for the response, your auth headers, and a classifyStatus for host-specific quirks (e.g. classify429(host) for rate limits). Never throw across the boundary; return Result. Mint SHAs only via sha40().
  3. Build a VersionSet from fetched tags/branches (VersionSet.build(...)) so resolution.ts can evaluate refs.
  4. Register it. In packages/providers/src/index.ts, add the new ProviderKind to the make() switch, the default token env in DEFAULT_TOKEN_ENV, and any host-label heuristics in guessKind(). The ProviderKind union itself lives in packages/core/src/provider.ts.
  5. Test behavior. Add tests/index.test.ts covering version resolution, SHA resolution, and the error mappings (auth, notFound, rateLimit, network). Tests should assert semantically correct behavior; a failing test that exposes a real bug is acceptable.
  6. Run bun run typecheck, bun test, bun run fmt before opening a PR.

VCS

Do not add bot/assistant attribution to commits or PRs. Keep changes minimal and surgical. See architecture for the data flow your provider plugs into.