Skip to content

A Beginner's Guide

This guide walks through the full club pipeline end to end. By the time you’re done, you’ll have a running club server, a package you published from your laptop, and a second project that depends on it.

Overview

You’ll go through four steps, in order:

  1. Self-host club with Docker — run the server locally using a docker-compose.yml that pulls the official image.
  2. Install the CLI — one-line install of the club binary.
  3. Publish your first package — log in, create a package, and push it to your server.
  4. Use your package in another project — add the published package as a dependency and import it.

A fifth section at the end shows how to automate publishing with GitHub Actions. Skip it on the first pass if you just want the happy path.

Prerequisites

  • Docker and Docker Compose (for the server)
  • Dart SDK 3.0+ (for creating and publishing packages)
  • Linux or macOS (the install script doesn’t support Windows — see the CLI installation guide for the manual path)

Throughout this guide, the local server runs at http://localhost:8080. In production you’d replace that with a real HTTPS URL.


Step 1 — Self-host club with Docker

The quickest way to get a working server is a two-file Docker Compose setup that pulls the official image from GitHub Container Registry. No repo clone required.

  1. Create a project directory

    Terminal window
    mkdir -p ~/club-server && cd ~/club-server
  2. Create docker-compose.yml

    services:
    club:
    image: ghcr.io/birjuvachhani/club:latest
    container_name: club
    restart: unless-stopped
    ports:
    - "127.0.0.1:8080:8080"
    env_file: .env
    volumes:
    - club_data:/data
    healthcheck:
    test: ["CMD", "curl", "-f", "http://localhost:8080/api/v1/health"]
    interval: 30s
    timeout: 5s
    retries: 3
    start_period: 10s
    volumes:
    club_data:
  3. Create .env

    Terminal window
    cat > .env <<EOF
    SERVER_URL=http://localhost:8080
    JWT_SECRET=$(openssl rand -hex 32)
    EOF
  4. Start the server

    Terminal window
    docker compose up -d
  5. Verify it’s running

    Terminal window
    curl http://localhost:8080/api/v1/health
    # {"status":"ok", ...}
  6. Create the owner account via the setup wizard

    On first startup the server prints a one-time setup code to the logs. You use it to create the initial owner account through the web UI.

    Terminal window
    docker compose logs club | grep -i "setup code"

    Open http://localhost:8080/setup, enter the setup code, then pick your email, display name, and password. The account is created with the owner role.


Step 2 — Install the CLI

The club CLI is a native binary. One-line install:

Terminal window
curl -fsSL https://club.birju.dev/install.sh | bash

The script detects your OS and CPU, downloads the right archive, verifies its SHA-256, and drops the binary at ~/.local/bin/club. If that directory isn’t on your PATH, the installer prints the one-liner to add to your shell’s rc file.

Verify:

Terminal window
club --version

For Windows, manual downloads, version pinning, or building from source, see the CLI installation guide.


Step 3 — Publish your first package

With the server running and the CLI installed, you’ll now log in, create a small package, and push it up.

  1. Log in to your server

    Terminal window
    club login http://localhost:8080

    This opens your browser to complete an OAuth authorization flow (PKCE). Sign in with the owner account you created in Step 1, approve the CLI’s read,write request, and the CLI stores a scoped personal access token in ~/.config/club/credentials.json.

  2. Register the token with dart pub

    Terminal window
    club setup

    Behind the scenes this runs dart pub token add http://localhost:8080, so the Dart SDK knows how to authenticate when you publish.

  3. Create a package

    Terminal window
    mkdir -p ~/hello_club && cd ~/hello_club

    Create pubspec.yaml:

    name: hello_club
    version: 1.0.0
    description: My first club package.
    publish_to: http://localhost:8080
    environment:
    sdk: ^3.0.0

    Create lib/hello_club.dart:

    /// A friendly greeting.
    String greet(String name) => 'Hello, $name!';
  4. Publish

    You have two options:

    Terminal window
    # Option A: the club CLI (runs ~25 preflight validators)
    club publish
    # Option B: vanilla dart pub
    dart pub publish --force

    club publish is the recommended path — it validates your package before upload (changelog, SDK constraints, README, dependencies, git status, size, etc.) and works without needing publish_to: in pubspec.yaml (it picks up your default club server automatically).

    Open http://localhost:8080 in your browser — hello_club 1.0.0 should appear on the packages page.

For the longer walkthrough with READMEs, changelogs, and troubleshooting, see Publishing Your First Package and the Publishing guide.


Step 4 — Use your package in another project

You publish packages so other projects can depend on them. Any developer who wants to consume packages from your club server goes through the same CLI setup: install, log in, then add a hosted dependency.

  1. Install and log in (skip if you already did steps 2–3 above)

    Terminal window
    curl -fsSL https://club.birju.dev/install.sh | bash
    club login http://localhost:8080
    club setup
  2. Create a project

    Terminal window
    mkdir -p ~/my_app && cd ~/my_app
    dart create -t console .
  3. Add hello_club as a hosted dependency

    Edit pubspec.yaml:

    dependencies:
    hello_club:
    hosted: http://localhost:8080
    version: ^1.0.0

    The hosted field tells dart pub to fetch this package from your club server. All other dependencies (no hosted field) continue to come from pub.dev as normal.

  4. Fetch and use it

    Terminal window
    dart pub get

    Edit bin/my_app.dart:

    import 'package:hello_club/hello_club.dart';
    void main() {
    print(greet('Developer'));
    }
    Terminal window
    dart run
    # Hello, Developer!

For version pinning, upgrading, and mixing private and public packages, see Using Packages.


Bonus — Automate with GitHub Actions

In CI you don’t want to log in interactively — you want a scoped, machine-owned token. The pattern is:

  1. Create an API key in the web dashboard
  2. Store it as a CI secret
  3. Register it with dart pub at runtime using --env-var

Requirements

  • A club API key with the right scopes
    • read only — if CI just runs dart pub get / tests
    • read + write — if CI runs dart pub publish
  • The secret stored in your CI provider’s secret store (GitHub Actions: Settings → Secrets and variables → Actions)
  • A reachable server URL (CI runners must be able to reach your club server)

Create the API key

  1. Open the web UI and go to Settings → API keys (the /settings/keys page).
  2. Click Create key, give it a name like github-actions-publish, and select the scopes you need (read + write for a publish workflow). Scopes are clamped to your user role — a member cannot mint admin tokens.
  3. Optionally set an expiry (90 days is a good default — rotate rather than forget).
  4. Copy the full key (club_pat_...) — it is only shown once.
  5. In GitHub, add it as a repository secret named CLUB_TOKEN.

Publishing workflow

This workflow publishes a new version whenever you push a v* tag.

.github/workflows/publish.yml
name: Publish
on:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dart-lang/setup-dart@v1
- name: Register club token with dart pub
run: dart pub token add https://club.example.com --env-var CLUB_TOKEN
env:
CLUB_TOKEN: ${{ secrets.CLUB_TOKEN }}
- name: Publish
run: dart pub publish --force
env:
CLUB_TOKEN: ${{ secrets.CLUB_TOKEN }}

The key step is dart pub token add ... --env-var CLUB_TOKEN. The --env-var form does not write the token to disk — it tells dart pub to read it from the named environment variable at runtime, which is exactly what you want in CI.

Typical release flow

  1. Bump the version in pubspec.yaml.

  2. Commit and tag:

    Terminal window
    git commit -am "Release v1.1.0"
    git tag v1.1.0
    git push origin main --tags
  3. GitHub Actions picks up the tag, runs the workflow, and the new version appears on your club server.

For GitLab, Bitbucket, caching, and security best practices, see the full CI/CD Integration guide.


Where to go next

You now have the complete pipeline: a server, a published package, a consuming project, and automated publishing. Good follow-ups: