Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e92dd46
docs: add CLI refactor design spec
Sep 11, 2026
c23839e
docs(spec): shared AddDatabaseFlow, generic engine options, split red…
Sep 11, 2026
8acecd9
docs: add plan 1 (CI hygiene, pinned actions, bump workflow)
Sep 11, 2026
43a9749
docs: add plan 2 (foundations: errors, ui, services, Command; lifecyc…
Sep 11, 2026
a7cf34b
docs: add plan 3 (Jinja2 templates, engine registry, template reposit…
Sep 11, 2026
333db61
docs: add plan 4 (declarative rendering, flag-driven commands, build)
Sep 11, 2026
394b25d
refactoring
Sep 11, 2026
17b56fe
feat(agent): show the proposed configuration and confirm before writing
Sep 11, 2026
d823cde
docs: dashboard settings and auth providers design
Sep 11, 2026
1b31db7
style: name the pre-write panel SUMMARY
Sep 11, 2026
c59cb08
feat!: one namespace per component — agent create, agent db
Sep 11, 2026
6627fc8
feat(dashboard): settings registry, auth providers and lock-out checks
Sep 11, 2026
00d0e79
feat(dashboard): create/show/set/unset and auth add/list/remove
Sep 11, 2026
5e0188c
feat(agent): show/set/unset, and every documented agent variable
Sep 11, 2026
f35b34b
feat(agent): ca_bundle mounts an internal CA and points SSL_CERT_FILE…
Sep 11, 2026
86089dc
refactor(agent): keep only SSL_CERT_FILE in .env; the bundle path liv…
Sep 11, 2026
4658f74
feat(dashboard): end create with a hint on adding OIDC/OAuth providers
Sep 11, 2026
ea37e4e
feat(dashboard): prompt for provider kind and id in auth add/remove
Sep 11, 2026
932b942
chore: drop a leftover comment in main.py
Sep 11, 2026
8d1ef4e
feat(dashboard): prompt for provider kind and id in auth add/remove
Sep 11, 2026
77167e5
add: new cli
Sep 14, 2026
bccb5ab
Merge branch 'main' into feat/refactoring
Sep 14, 2026
47096dd
Merge remote-tracking branch 'origin/feat/refactoring' into feat/refa…
Sep 14, 2026
788e9e5
ci: track the build action, drop SARIF upload, pass gitleaks config v…
Sep 14, 2026
db29462
Create .gitleaksignore
Sep 14, 2026
1b0d326
Update plumber.yml
Sep 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,15 @@ uv run python main.py config channel stable # or: beta
uv run python main.py update
```

### Running the tests

Unit tests live in `tests/`, mirroring `core/`, `services/` and `engines/`. They call
the functions directly: no Docker, no network, no built binary.

```bash
uv run pytest
uv run ruff check . && uv run ruff format --check . && uv run mypy
```
---

## Reporting Issues
Expand Down Expand Up @@ -237,4 +246,16 @@ If you encounter a bug or have a suggestion for improvement, follow these steps:

Thank you for contributing! 🙌

---
---
## Releasing

Releases are cut from GitHub Actions, never from a local machine.

1. Open **Actions → Bump version → Run workflow**.
2. Pick the branch (`main` for stable, any branch for a release candidate).
3. Enter the version without a leading `v` (`26.09.0` for stable, `26.09.0rc1` for a candidate) and the matching channel.
4. The workflow commits `chore(release): <version>`, creates the tag and pushes. The tag triggers the build, the GitHub release, the Discord notification and the template upload.

Stable versions must match `X.Y.Z` and can only be cut from `main`.

The workflow pushes with the `RELEASE_TOKEN` repository secret (a fine-grained PAT with *Contents: read and write*). A tag pushed with the default `GITHUB_TOKEN` would not trigger the release workflows.
45 changes: 45 additions & 0 deletions .github/actions/build/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Build the CLI binary
description: >-
Build the standalone binary from portabase.spec and check that it can read
its own bundled metadata. A binary that reports no version cannot resolve
its templates either, so it is rejected here rather than shipped.

inputs:
name:
description: Binary name, without extension.
required: false
default: portabase

outputs:
path:
description: Path to the built binary.
value: ${{ steps.build.outputs.path }}

runs:
using: composite
steps:
- id: build
shell: bash
env:
PORTABASE_BINARY_NAME: ${{ inputs.name }}
run: |
set -euo pipefail
rm -rf build dist
uv run pyinstaller portabase.spec

case "$(uname -s)" in
MINGW* | MSYS* | CYGWIN*) EXT=".exe" ;;
*) EXT="" ;;
esac
BINARY="dist/${PORTABASE_BINARY_NAME}${EXT}"
test -f "$BINARY"
echo "path=$BINARY" >> "$GITHUB_OUTPUT"

- shell: bash
run: |
set -euo pipefail
VERSION=$("${{ steps.build.outputs.path }}" --version | head -1)
echo "$VERSION"
case "$VERSION" in
*unknown*) echo "::error::binary cannot read its bundled version"; exit 1 ;;
esac
13 changes: 13 additions & 0 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: Set up Python toolchain
description: Install uv and sync the locked environment, including dev dependencies.

runs:
using: composite
steps:
- uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3
with:
enable-cache: true

- name: Install dependencies
shell: bash
run: uv sync --frozen --all-groups
22 changes: 0 additions & 22 deletions .github/assets/templates/agent.yml

This file was deleted.

46 changes: 0 additions & 46 deletions .github/assets/templates/dashboard.yml

This file was deleted.

21 changes: 21 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
groups:
actions:
patterns: ["*"]
commit-message:
prefix: "ci"

- package-ecosystem: uv
directory: /
schedule:
interval: weekly
groups:
python:
patterns: ["*"]
commit-message:
prefix: "build"
62 changes: 62 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Build binaries

on:
workflow_call:

permissions: {}

jobs:
build:
name: ${{ matrix.os }}-${{ matrix.arch }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 20
permissions:
contents: read
id-token: write
attestations: write
strategy:
fail-fast: false
matrix:
include:
- os: linux
arch: amd64
runner: ubuntu-latest
- os: linux
arch: arm64
runner: ubuntu-24.04-arm
- os: macos
arch: arm64
runner: macos-latest
- os: macos
arch: amd64
runner: macos-15-intel
- os: windows
arch: amd64
runner: windows-latest
ext: .exe
defaults:
run:
shell: bash
env:
NAME: portabase_${{ matrix.os }}_${{ matrix.arch }}
BINARY: portabase_${{ matrix.os }}_${{ matrix.arch }}${{ matrix.ext }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: ./.github/actions/setup

- name: Build
id: build
uses: ./.github/actions/build
with:
name: ${{ env.NAME }}

- name: Attest provenance
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2
with:
subject-path: ${{ steps.build.outputs.path }}

- name: Upload
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ env.BINARY }}
path: ${{ steps.build.outputs.path }}
85 changes: 85 additions & 0 deletions .github/workflows/bump.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: Bump version

on:
workflow_dispatch:
inputs:
version:
description: "Version (e.g. 26.09.0 or 26.09.0rc1). No leading v."
required: true
type: string
channel:
description: "stable: only from main. rc: any branch."
required: true
type: choice
options: [stable, rc]
default: rc

permissions: {}

jobs:
bump:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
fetch-depth: 0
token: ${{ secrets.RELEASE_TOKEN }}

- name: Validate version against channel
env:
VERSION: ${{ inputs.version }}
CHANNEL: ${{ inputs.channel }}
REF: ${{ github.ref_name }}
run: |
set -euo pipefail
if [[ "$VERSION" == v* ]]; then
echo "::error::Version must not start with 'v'"; exit 1
fi
if [[ "$CHANNEL" == "stable" ]]; then
if [[ "$REF" != "main" ]]; then
echo "::error::stable releases are only allowed from main (got $REF)"; exit 1
fi
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::stable version must match X.Y.Z"; exit 1
fi
else
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-.]?(rc|alpha|beta|a|b)[0-9]*(\.[0-9]+)?)$ ]]; then
echo "::error::rc version must match X.Y.Z(rc|a|b|alpha|beta)N"; exit 1
fi
fi
if git rev-parse "$VERSION" >/dev/null 2>&1; then
echo "::error::Tag $VERSION already exists"; exit 1
fi

- name: Update version files
env:
VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
DATE=$(date -u +%F)
sed -i "s/^version = \".*\"/version = \"$VERSION\"/" pyproject.toml
if [ -f CITATION.cff ]; then
sed -i "s/^version: .*/version: $VERSION/" CITATION.cff
sed -i "s/^date-released: .*/date-released: \"$DATE\"/" CITATION.cff
fi
git diff --stat

- name: Commit, tag, push
env:
VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add pyproject.toml CITATION.cff
if git diff --cached --quiet; then
echo "No version change to commit"
else
git commit -m "chore(release): $VERSION"
fi
git tag -a "$VERSION" -m "Release $VERSION"
git push origin HEAD
git push origin "$VERSION"
50 changes: 50 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: CI

on:
pull_request:
push:
branches: [main]

permissions: {}

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
checks:
name: ${{ matrix.name }}
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- name: lint
run: uv run ruff check . --output-format=github
- name: format
run: uv run ruff format --check .
- name: types
run: uv run mypy
- name: tests
run: uv run pytest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: ./.github/actions/setup
- run: ${{ matrix.run }}

binary:
name: binary
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: ./.github/actions/setup

- name: Build
id: build
uses: ./.github/actions/build
Loading
Loading