tapes
Package main provides reproducible builds and tests locally and in GitHub actions.It is the main harness for handling nearly all dev operations.
Installation
dagger install github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7fEntrypoint
Return Type
Tapes !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory | - | Project source directory. |
Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
func (m *MyModule) Example() *dagger.Tapes {
return dag.
Tapes()
}@function
def example() -> dagger.Tapes:
return (
dag.tapes()
)@func()
example(): Tapes {
return dag
.tapes()
}Types
Tapes 🔗
Tapes is the main module for the Tapes CI/CD pipeline
build() 🔗
Build and return directory of go binaries for all platforms.
Return Type
Directory !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| ldflags | String | "-s -w" | Linker flags for go build |
Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
buildfunc (m *MyModule) Example() *dagger.Directory {
return dag.
Tapes().
Build()
}@function
def example() -> dagger.Directory:
return (
dag.tapes()
.build()
)@func()
example(): Directory {
return dag
.tapes()
.build()
}buildRelease() 🔗
BuildRelease compiles versioned release binaries with embedded version info
Return Type
Directory !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| version | String ! | - | Version string of build |
| commit | String ! | - | Git commit SHA of build |
| postHogPublicKey | String | - | PostHog telemetry public API key (write-only). Empty disables telemetry. |
| postHogEndpoint | String | - | PostHog ingestion endpoint |
Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
build-release --version string --commit stringfunc (m *MyModule) Example(version string, commit string) *dagger.Directory {
return dag.
Tapes().
Buildrelease(version, commit)
}@function
def example(version: str, commit: str) -> dagger.Directory:
return (
dag.tapes()
.buildrelease(version, commit)
)@func()
example(version: string, commit: string): Directory {
return dag
.tapes()
.buildRelease(version, commit)
}checkExtproc() 🔗
CheckExtproc runs the ext_proc adapter’s test suite.
It is separate from Test, and bound to no database, because none of these tests need one — extproc talks to ingest over HTTP and is tested against fixtures. That is what makes it affordable on every PR, which matters: Test binds Postgres and for that reason does not run on the PR path, so folding extproc’s tests into it would have quietly retired the gate that used to run on every extproc change. This keeps that gate, including the wire-capture fidelity tests, which read the committed recordings and therefore always run rather than skipping.
Return Type
String ! Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
check-extprocfunc (m *MyModule) Example(ctx context.Context) string {
return dag.
Tapes().
Checkextproc(ctx)
}@function
async def example() -> str:
return await (
dag.tapes()
.checkextproc()
)@func()
async example(): Promise<string> {
return dag
.tapes()
.checkExtproc()
}checkGenerate() 🔗
CheckGenerate verifies that sqlc-generated code is up to date by regenerating it from sqlc.yaml and diffing the result against the committed files.
Return Type
String ! Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
check-generatefunc (m *MyModule) Example(ctx context.Context) string {
return dag.
Tapes().
Checkgenerate(ctx)
}@function
async def example() -> str:
return await (
dag.tapes()
.checkgenerate()
)@func()
async example(): Promise<string> {
return dag
.tapes()
.checkGenerate()
}checkGoModTidy() 🔗
CheckGoModTidy runs “go mod tidy” and fails if it produces any changes to go.mod or go.sum, indicating that the caller forgot to tidy before committing.
Return Type
String ! Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
check-go-mod-tidyfunc (m *MyModule) Example(ctx context.Context) string {
return dag.
Tapes().
Checkgomodtidy(ctx)
}@function
async def example() -> str:
return await (
dag.tapes()
.checkgomodtidy()
)@func()
async example(): Promise<string> {
return dag
.tapes()
.checkGoModTidy()
}checkLint() 🔗
CheckLint runs golangci-lint against the tapes source code without applying fixes.
Return Type
String ! Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
check-lintfunc (m *MyModule) Example(ctx context.Context) string {
return dag.
Tapes().
Checklint(ctx)
}@function
async def example() -> str:
return await (
dag.tapes()
.checklint()
)@func()
async example(): Promise<string> {
return dag
.tapes()
.checkLint()
}checkParity() 🔗
CheckParity runs the contract-fixture gates: the envelope corpus parity gate (seal, schema, coverage), the parser oracle that holds this repo’s reader to that corpus, and the envelope validation rules the corpus’s error cases declare.
It exists as a check of its own rather than as part of Test for two reasons.
The first is that it needs nothing. No Postgres, no Ollama, no services at all — it is fixtures and pure functions. Test binds a database because most of the suite needs one, and that cost is why Test is not on the PR path today; CI runs TestE2E, Build and Smoke. That left the envelope contract — which is the agreement between capture paths in different repositories — gated by nothing on a pull request, even though a corpus edit is exactly the kind of change whose blast radius reaches other repos. A check with no service dependencies can run on every PR in seconds.
The second is what a failure here means. A red CheckParity says the shared contract moved, and the fix usually involves another repository: re-sync a vendored copy, or land a parser change beside the fixture change. Keeping it separate makes that legible from the check name alone, instead of arriving as one failed assertion inside a full unit-test run.
Return Type
String ! Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
check-parityfunc (m *MyModule) Example(ctx context.Context) string {
return dag.
Tapes().
Checkparity(ctx)
}@function
async def example() -> str:
return await (
dag.tapes()
.checkparity()
)@func()
async example(): Promise<string> {
return dag
.tapes()
.checkParity()
}contracts() 🔗
Contracts compiles both published OpenAPI documents and returns them as a directory: tapes-api.yaml and tapes-ingest.yaml.
These are the documents consumers vendor. Nothing in this repository reads them, and they are not checked in — a contract file is a copy of what the server already states, and a copy can go stale. What is checked in is the fingerprint of each (api/CONTRACT, ingest/CONTRACT), which is a copy that cannot go stale quietly because a spec recompiles and compares it.
Prose is INCLUDED here, the opposite of what the seals cover. The two want
opposite things for the same reason: a seal should fire only when the shape
changes, so it excludes comments; a vendored document is read by a person
writing a client, so it carries every field’s prose. --docs-root . is what
folds those comments in, and it needs a checkout — which is why this runs in
the source container rather than being something a released binary could do.
The documents are CORE only. Neither is the aggregate a running server publishes at /openapi, which is this document with each mounted cassette’s spec merged in and therefore a function of deployment rather than of release.
Return Type
Directory ! Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
contractsfunc (m *MyModule) Example() *dagger.Directory {
return dag.
Tapes().
Contracts()
}@function
def example() -> dagger.Directory:
return (
dag.tapes()
.contracts()
)@func()
example(): Directory {
return dag
.tapes()
.contracts()
}fixLint() 🔗
FixLint runs golangci-lint against the tapes source code with –fix, applying automatic fixes where possible, and returns the modified source directory.
Return Type
Directory ! Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
fix-lintfunc (m *MyModule) Example() *dagger.Directory {
return dag.
Tapes().
Fixlint()
}@function
def example() -> dagger.Directory:
return (
dag.tapes()
.fixlint()
)@func()
example(): Directory {
return dag
.tapes()
.fixLint()
}nightly() 🔗
Nightly builds and uploads nightly artifacts
Return Type
Directory !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| commit | String ! | - | Git commit SHA |
| postHogPublicKey | String | - | PostHog telemetry public API key (write-only) |
| endpoint | Secret ! | - | Bucket endpoint URL |
| bucket | Secret ! | - | Bucket name |
| accessKeyId | Secret ! | - | Bucket access key ID |
| secretAccessKey | Secret ! | - | Bucket secret access key |
Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
nightly --commit string --endpoint env:MYSECRET --bucket env:MYSECRET --access-key-id env:MYSECRET --secret-access-key env:MYSECRETfunc (m *MyModule) Example(commit string, endpoint *dagger.Secret, bucket *dagger.Secret, accessKeyId *dagger.Secret, secretAccessKey *dagger.Secret) *dagger.Directory {
return dag.
Tapes().
Nightly(commit, endpoint, bucket, accessKeyId, secretAccessKey)
}@function
def example(commit: str, endpoint: dagger.Secret, bucket: dagger.Secret, accesskeyid: dagger.Secret, secretaccesskey: dagger.Secret) -> dagger.Directory:
return (
dag.tapes()
.nightly(commit, endpoint, bucket, accesskeyid, secretaccesskey)
)@func()
example(commit: string, endpoint: Secret, bucket: Secret, accessKeyId: Secret, secretAccessKey: Secret): Directory {
return dag
.tapes()
.nightly(commit, endpoint, bucket, accessKeyId, secretAccessKey)
}ollamaPullModel() 🔗
ollamaPullModel pulls a given Ollama model in a sidecare container.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| model | String ! | - | No description provided |
| ollamaSvc | Service ! | - | No description provided |
Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
ollama-pull-model --model string --ollama-svc PROTOCOL://HOST:PORTfunc (m *MyModule) Example(ctx context.Context, model string, ollamaSvc *dagger.Service) string {
return dag.
Tapes().
Ollamapullmodel(ctx, model, ollamaSvc)
}@function
async def example(model: str, ollamasvc: dagger.Service) -> str:
return await (
dag.tapes()
.ollamapullmodel(model, ollamasvc)
)@func()
async example(model: string, ollamaSvc: Service): Promise<string> {
return dag
.tapes()
.ollamaPullModel(model, ollamaSvc)
}ollamaService() 🔗
OllamaService provides an Ollama ready to run service. This service uses a cache volume so models are only pulled once across runs. Pre-create the models/manifests directory tree so Ollama’s serve command doesn’t crash on a fresh (empty) cache volume.
Return Type
Service ! Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
ollama-servicefunc (m *MyModule) Example() *dagger.Service {
return dag.
Tapes().
Ollamaservice()
}@function
def example() -> dagger.Service:
return (
dag.tapes()
.ollamaservice()
)@func()
example(): Service {
return dag
.tapes()
.ollamaService()
}ollamaStack() 🔗
Return Type
Service ! Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
ollama-stackfunc (m *MyModule) Example() *dagger.Service {
return dag.
Tapes().
Ollamastack()
}@function
def example() -> dagger.Service:
return (
dag.tapes()
.ollamastack()
)@func()
example(): Service {
return dag
.tapes()
.ollamaStack()
}postgresService() 🔗
PostgresService provides a ready to run postgres service with “tapes” user, password, and db
Return Type
Service ! Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
postgres-servicefunc (m *MyModule) Example() *dagger.Service {
return dag.
Tapes().
Postgresservice()
}@function
def example() -> dagger.Service:
return (
dag.tapes()
.postgresservice()
)@func()
example(): Service {
return dag
.tapes()
.postgresService()
}releaseLatest() 🔗
Release builds release binaries and uploads to bucket
Return Type
Directory !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| version | String ! | - | Version string (e.g., “v1.0.0”) |
| commit | String ! | - | Git commit SHA |
| postHogPublicKey | String | - | PostHog telemetry public API key (write-only) |
| endpoint | Secret ! | - | Bucket endpoint URL |
| bucket | Secret ! | - | Bucket name |
| accessKeyId | Secret ! | - | Bucket access key ID |
| secretAccessKey | Secret ! | - | Bucket secret access key |
Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
release-latest --version string --commit string --endpoint env:MYSECRET --bucket env:MYSECRET --access-key-id env:MYSECRET --secret-access-key env:MYSECRETfunc (m *MyModule) Example(version string, commit string, endpoint *dagger.Secret, bucket *dagger.Secret, accessKeyId *dagger.Secret, secretAccessKey *dagger.Secret) *dagger.Directory {
return dag.
Tapes().
Releaselatest(version, commit, endpoint, bucket, accessKeyId, secretAccessKey)
}@function
def example(version: str, commit: str, endpoint: dagger.Secret, bucket: dagger.Secret, accesskeyid: dagger.Secret, secretaccesskey: dagger.Secret) -> dagger.Directory:
return (
dag.tapes()
.releaselatest(version, commit, endpoint, bucket, accesskeyid, secretaccesskey)
)@func()
example(version: string, commit: string, endpoint: Secret, bucket: Secret, accessKeyId: Secret, secretAccessKey: Secret): Directory {
return dag
.tapes()
.releaseLatest(version, commit, endpoint, bucket, accessKeyId, secretAccessKey)
}test() 🔗
Test runs the tapes unit tests via “go test” and provides a just-in-time Postgres service that can be used by the tests via the “TEST_POSTGRES_DSN” env var.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| runId | String | - | Inert identifier that callers can vary to bypass Dagger’s exec cache. |
Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
testfunc (m *MyModule) Example(ctx context.Context) string {
return dag.
Tapes().
Test(ctx)
}@function
async def example() -> str:
return await (
dag.tapes()
.test()
)@func()
async example(): Promise<string> {
return dag
.tapes()
.test()
}testE2E() 🔗
TestE2E runs end-to-end tests against Postgres and Ollama services.
It stands up a PostgreSQL database and an Ollama LLM service, builds the tapes binary, runs the proxy and API as Dagger services backed by Postgres, and uses hurl to verify the full pipeline.
Return Type
String ! Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
test-e-2-efunc (m *MyModule) Example(ctx context.Context) string {
return dag.
Tapes().
Teste2e(ctx)
}@function
async def example() -> str:
return await (
dag.tapes()
.teste2e()
)@func()
async example(): Promise<string> {
return dag
.tapes()
.testE2E()
}uploadInstallSh() 🔗
UploadInstallSh uploads the install.sh script to the artifacts bucket
Return Type
Void !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| endpoint | Secret ! | - | Bucket endpoint URL |
| bucket | Secret ! | - | Bucket name |
| accessKeyId | Secret ! | - | Bucket access key ID |
| secretAccessKey | Secret ! | - | Bucket secret access key |
Example
dagger -m github.com/papercomputeco/tapes@0cc40ead9c895d2893cc4f70fa078c2c075c9d7f call \
upload-install-sh --endpoint env:MYSECRET --bucket env:MYSECRET --access-key-id env:MYSECRET --secret-access-key env:MYSECRETfunc (m *MyModule) Example(ctx context.Context, endpoint *dagger.Secret, bucket *dagger.Secret, accessKeyId *dagger.Secret, secretAccessKey *dagger.Secret) {
return dag.
Tapes().
Uploadinstallsh(ctx, endpoint, bucket, accessKeyId, secretAccessKey)
}@function
async def example(endpoint: dagger.Secret, bucket: dagger.Secret, accesskeyid: dagger.Secret, secretaccesskey: dagger.Secret) -> None:
return await (
dag.tapes()
.uploadinstallsh(endpoint, bucket, accesskeyid, secretaccesskey)
)@func()
async example(endpoint: Secret, bucket: Secret, accessKeyId: Secret, secretAccessKey: Secret): Promise<void> {
return dag
.tapes()
.uploadInstallSh(endpoint, bucket, accessKeyId, secretAccessKey)
}