Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
72 changes: 66 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,41 @@ jobs:
# Without it the suite still passes, but the Swiss golden tests and the
# geometry gate skip, which would make this workflow green while proving
# nothing about the real calculation path.
# GitHub Actions caches are immutable. A static key that once stored
# incomplete files will keep restoring them. Hash the workflow so the
# cache invalidates when the fetch instructions change, and use v2 so
# the previous incomplete v1 entry is not reused.
- name: Cache ephemeris data
id: ephe-cache
uses: actions/cache@v4
with:
path: .ephemeris
key: ephe-swiss18-de440s-v1
key: ephe-swiss18-de440s-v2-${{ hashFiles('.github/workflows/ci.yml') }}
restore-keys: |
ephe-swiss18-de440s-v2-

# DE440S is needed by the lunar-node reference tests, not only by the
# weekly validation workflow. Without it those tests would skip, and the
# skip guard below turns that into a failure rather than a quiet pass.
# Fetch any missing or empty files even on a cache hit, so a restored
# incomplete cache cannot skip the download.
- name: Fetch ephemeris data
if: steps.ephe-cache.outputs.cache-hit != 'true'
run: |
mkdir -p .ephemeris/swiss .ephemeris/jpl
base=https://raw.githubusercontent.com/aloistr/swisseph/master/ephe
for file in sepl_18.se1 semo_18.se1 seas_18.se1; do
curl -fsSL -o ".ephemeris/swiss/$file" "$base/$file"
dest=".ephemeris/swiss/$file"
if [ ! -s "$dest" ]; then
curl -fsSL -o "$dest" "$base/$file"
fi
done
curl -fsSL -o .ephemeris/jpl/de440s.bsp \
https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/de440s.bsp
if [ ! -s .ephemeris/jpl/de440s.bsp ]; then
curl -fsSL -o .ephemeris/jpl/de440s.bsp \
https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/de440s.bsp
fi

- name: Install
run: python -m pip install -e ".[dev,swiss,validation]"
run: python -m pip install -e ".[dev,validation]"

- name: Assert ephemeris data is actually present
run: |
Expand All @@ -58,6 +70,54 @@ jobs:
echo "Missing de440s.bsp: the node reference tests would skip."
exit 1
}
# `test -s` accepts an HTML error page. Reject downloads that are
# far smaller than the real Swiss files before pytest spends a minute
# turning that into 26 HTTP 503s.
python - <<'PY'
import os
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime, timezone
from pathlib import Path

swiss = Path(os.environ["GBC_SWISS_EPHE_PATH"])
minimums = {
"sepl_18.se1": 400_000,
"semo_18.se1": 1_000_000,
"seas_18.se1": 200_000,
}
for name, minimum in minimums.items():
size = (swiss / name).stat().st_size
if size < minimum:
raise SystemExit(
f"{name} is {size} bytes; expected at least {minimum}. "
"The download is truncated or not a Swiss Ephemeris file."
)

from gbc_astro.providers.swiss import SwissEphemerisProvider

provider = SwissEphemerisProvider(ephemeris_path=str(swiss))
instant = datetime(1992, 11, 3, 7, 35, tzinfo=timezone.utc)
sun = ThreadPoolExecutor(max_workers=1).submit(
provider.position, "sun", instant
).result()
print(f"swiss_ok sun={sun.longitude_deg:.6f} from worker thread")

from fastapi.testclient import TestClient
from gbc_astro.api.app import create_app

natal = {
"local_date": "1992-11-03",
"local_time": "14:35:00",
"timezone": "Asia/Ho_Chi_Minh",
"latitude": 21.0285,
"longitude": 105.8542,
}
with TestClient(create_app()) as client:
response = client.post("/v1/charts/natal", json=natal)
if response.status_code != 200:
raise SystemExit(f"natal HTTP {response.status_code}: {response.text}")
print("natal_http_ok")
PY

- name: Ruff
run: ruff check .
Expand Down
21 changes: 14 additions & 7 deletions .github/workflows/v01-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,32 @@ jobs:
uses: actions/cache@v4
with:
path: .ephemeris
key: ephe-swiss18-de440s-v1
key: ephe-swiss18-de440s-v2-${{ hashFiles('.github/workflows/v01-validation.yml') }}
restore-keys: |
ephe-swiss18-de440s-v2-

# Neither data set is committed: both carry redistribution terms.
# Fetch any missing or empty files even on a cache hit.
- name: Fetch ephemeris data
if: steps.ephe-cache.outputs.cache-hit != 'true'
run: |
mkdir -p .ephemeris/swiss .ephemeris/jpl
base=https://raw.githubusercontent.com/aloistr/swisseph/master/ephe
for file in sepl_18.se1 semo_18.se1 seas_18.se1; do
curl -fsSL -o ".ephemeris/swiss/$file" "$base/$file"
dest=".ephemeris/swiss/$file"
if [ ! -s "$dest" ]; then
curl -fsSL -o "$dest" "$base/$file"
fi
done
curl -fsSL -o .ephemeris/jpl/de440s.bsp \
https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/de440s.bsp
if [ ! -s .ephemeris/jpl/de440s.bsp ]; then
curl -fsSL -o .ephemeris/jpl/de440s.bsp \
https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/de440s.bsp
fi

# The JPL track needs skyfield/jplephem/numpy from the validation extra.
# Installing only [dev,swiss] leaves the reference unavailable, which used
# Installing only [dev] leaves the reference unavailable, which used
# to make this workflow report success without comparing anything.
- name: Install
run: python -m pip install -e ".[dev,swiss,validation]"
run: python -m pip install -e ".[dev,validation]"

- name: Assert ephemeris data is actually present
run: |
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Changelog

## 1.12.1 — 2026-08-22

PyPI packaging of GetBirthChart Core `1.12.1`.

- Public facade: `calculate_chart`, plus `calculate_planet_positions`,
`calculate_houses`, `calculate_aspects`, `get_zodiac_sign`, and
`normalize_angle`
- Package version, `ENGINE_VERSION`, and `gbc_astro.__version__` are `1.12.1`
- Natal schema remains `1.3.0`
- `pyswisseph` is a runtime dependency; Swiss Ephemeris `.se1` files are not
shipped
- Unknown birth time omits angles and houses; no noon substitution
- FastAPI adapter source ships in the wheel; `fastapi`/`uvicorn` remain behind
the optional `[api]` extra
- License remains AGPL-3.0-only
22 changes: 22 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
cff-version: 1.2.0
title: gbc-astro
message: If you use this software, please cite it using these metadata.
type: software
authors:
- family-names: Pham
given-names: Luis
website: "https://getbirthchart.com/author/luis-pham/"
repository-code: "https://github.com/getbirthchart-com/gbc-astro-engine"
url: "https://getbirthchart.com/"
license: AGPL-3.0-only
version: "1.12.1"
abstract: >-
A Python astrology calculation engine powered by Swiss Ephemeris.
gbc-astro is the installable Python package for the open-source
GetBirthChart astrology calculation engine.
keywords:
- astrology
- birth-chart
- natal-chart
- ephemeris
- swiss-ephemeris
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ FROM python:3.12-slim AS builder
WORKDIR /build
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 PIP_NO_CACHE_DIR=1

COPY pyproject.toml README.md ./
COPY pyproject.toml README.md LICENSE THIRD_PARTY_NOTICES.md ./
COPY src/ ./src/

# Build a wheel so the runtime stage carries no build toolchain.
Expand All @@ -31,7 +31,7 @@ ENV PYTHONUNBUFFERED=1 \
RUN --mount=type=bind,from=builder,source=/dist,target=/dist \
apt-get update \
&& apt-get install -y --no-install-recommends build-essential \
&& python -m pip install "$(ls /dist/*.whl)[api,swiss]" \
&& python -m pip install "$(ls /dist/*.whl)[api]" \
&& apt-get purge -y build-essential \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/*
Expand Down
38 changes: 38 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
include LICENSE
include THIRD_PARTY_NOTICES.md
include README.md
include CHANGELOG.md
include CITATION.cff
include pyproject.toml
recursive-include src/gbc_astro *.py
recursive-include tests *.py *.json
recursive-include scripts *.sh
include docs/CALCULATION_PROFILES.md
include docs/EPHEMERIS_AND_ASTEROIDS.md
include docs/EPHEMERIS_DATA.md
include docs/HOUSE_REFERENCE_METHODOLOGY.md
include docs/HOUSE_SYSTEMS.md
include docs/JPL_DATA.md
include docs/JPL_REFERENCE_METHODOLOGY.md
include docs/PATTERNS.md
include docs/PROGRESSIONS.md
include docs/PUBLIC_GOLDEN_TESTS.md
include docs/RELOCATION_AND_ACG.md
include docs/SIDEREAL.md
include docs/TRANSFORMS.md
include docs/TRANSITS.md
prune evidence
prune GetBirthChart_Astrology_Engine_Spec
prune gbc_synastry_core_docs
prune openapi
prune .github
prune .hypothesis
prune tests/api
exclude Dockerfile
exclude docker-compose.yml
exclude .dockerignore
exclude docs/API.md
exclude docs/DEPLOYMENT.md
exclude docs/FRONTEND_API_HANDOFF.md
exclude docs/FRONTEND_TRANSIT_HANDOFF.md
exclude docs/PRODUCTION_EPHEMERIS_SETUP.md
Loading
Loading