Skip to content

CI/CD Integration

club integrates cleanly with CI/CD pipelines. The basic recipe is:

  1. Create a Personal Access Token (PAT) scoped to exactly what the job needs.
  2. Store it as a CI secret (e.g. CLUB_TOKEN).
  3. Register it with dart pub using --env-var so the token never touches disk.
  4. Run dart pub get, dart pub publish, or club publish as usual.

Creating CI Tokens

Create a dedicated PAT from the web UI at /settings/keys with the minimum scopes:

Job typeScopes
Read-only (build, test, dart pub get)read
Publishing (dart pub publish, club publish)read + write

Optionally set an expiry (e.g. 90 days) so forgotten keys don’t linger. The full secret (club_pat_...) is shown once — copy it straight into your CI provider’s secrets store.

dart pub token add --env-var

The --env-var flag registers a token by environment variable name — the token itself is not written to disk. At resolution time dart pub reads the named env var:

Terminal window
dart pub token add https://club.example.com --env-var CLUB_TOKEN

With CLUB_TOKEN exported to the value club_pat_..., dart pub get and dart pub publish will attach it as a Bearer token automatically.

GitHub Actions

Fetching Packages

.github/workflows/build.yml
name: Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dart-lang/setup-dart@v1
- name: Configure club token
run: dart pub token add https://club.example.com --env-var CLUB_TOKEN
env:
CLUB_TOKEN: ${{ secrets.CLUB_TOKEN }}
- name: Get dependencies
run: dart pub get
env:
CLUB_TOKEN: ${{ secrets.CLUB_TOKEN }}
- name: Run tests
run: dart test

Publishing Packages

.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: Configure club token
run: dart pub token add https://club.example.com --env-var CLUB_TOKEN
env:
CLUB_TOKEN: ${{ secrets.CLUB_TOKEN }}
- name: Publish package
run: dart pub publish --force
env:
CLUB_TOKEN: ${{ secrets.CLUB_TOKEN }}

Flutter Projects

.github/workflows/flutter.yml
name: Flutter Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.x'
- name: Configure club token
run: dart pub token add https://club.example.com --env-var CLUB_TOKEN
env:
CLUB_TOKEN: ${{ secrets.CLUB_TOKEN }}
- name: Get dependencies
run: flutter pub get
env:
CLUB_TOKEN: ${{ secrets.CLUB_TOKEN }}
- name: Run tests
run: flutter test

GitLab CI

.gitlab-ci.yml
stages:
- build
- publish
variables:
CLUB_SERVER: https://club.example.com
build:
image: dart:stable
stage: build
before_script:
- dart pub token add $CLUB_SERVER --env-var CLUB_TOKEN
script:
- dart pub get
- dart test
cache:
key: dart-packages
paths:
- .dart_tool/
publish:
image: dart:stable
stage: publish
only:
- tags
before_script:
- dart pub token add $CLUB_SERVER --env-var CLUB_TOKEN
script:
- dart pub publish --force

Bitbucket Pipelines

bitbucket-pipelines.yml
image: dart:stable
pipelines:
default:
- step:
name: Build and Test
caches:
- dart-packages
script:
- dart pub token add https://club.example.com --env-var CLUB_TOKEN
- dart pub get
- dart test
tags:
'v*':
- step:
name: Publish
script:
- dart pub token add https://club.example.com --env-var CLUB_TOKEN
- dart pub publish --force
definitions:
caches:
dart-packages: .dart_tool/

CircleCI

.circleci/config.yml
version: 2.1
jobs:
build:
docker:
- image: cimg/dart:stable
steps:
- checkout
- run:
name: Configure club token
command: dart pub token add https://club.example.com --env-var CLUB_TOKEN
- run: dart pub get
- run: dart test
publish:
docker:
- image: cimg/dart:stable
steps:
- checkout
- run:
name: Configure club token
command: dart pub token add https://club.example.com --env-var CLUB_TOKEN
- run: dart pub publish --force
workflows:
test-and-publish:
jobs:
- build
- publish:
requires:
- build
filters:
tags:
only: /^v.*/
branches:
ignore: /.*/

Caching Strategies

Caching Dart dependencies speeds up CI builds significantly:

DirectoryContents
.dart_tool/Resolved package config and cached build artifacts
~/.pub-cache/Downloaded package archives

GitHub Actions Caching

.github/workflows/build.yml (cache step)
- name: Cache Dart packages
uses: actions/cache@v4
with:
path: |
~/.pub-cache
.dart_tool/
key: dart-${{ hashFiles('pubspec.lock') }}
restore-keys: |
dart-

GitLab CI Caching

cache:
key: dart-$CI_COMMIT_REF_SLUG
paths:
- .dart_tool/
- $PUB_CACHE

Security Best Practices

  1. Use minimal scopes. Read-only build jobs should use read only. Add write only for publish jobs.

  2. Set an expiry. Short-lived tokens (e.g. 90 days) limit the blast radius of a leak.

  3. Always use --env-var. Never hardcode tokens in pipeline files, Dockerfiles, or committed scripts.

  4. Mark secrets as masked. All major CI providers support masking secrets in logs.

  5. Separate read and write tokens. One token for build jobs (read-only), another for publish jobs (read + write).

  6. Revoke promptly. When a pipeline is decommissioned or a developer leaves, revoke the associated PATs from /settings/keys.