diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 0000000..fce4c42 --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,9 @@ +name: "CodeQL config" + +# CodeQL does not support Julia, so the only analyzable code in this +# repository is the GitHub Actions workflows (the `actions` language). +queries: + - uses: security-extended + +paths: + - .github diff --git a/.github/workflows/CodeQL.yml b/.github/workflows/CodeQL.yml new file mode 100644 index 0000000..ebbd5ca --- /dev/null +++ b/.github/workflows/CodeQL.yml @@ -0,0 +1,39 @@ +name: CodeQL + +on: + push: + branches: + - main + tags: ["*"] + pull_request: + schedule: + - cron: "31 4 * * 1" + +concurrency: + # Skip intermediate builds: always. + # Cancel intermediate builds: only if it is a pull request build. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} + +jobs: + + analyze: + name: Analyze (actions) + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + steps: + - name: Clone + uses: actions/checkout@v6 + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + # CodeQL does not support Julia; only the Actions workflows are analyzable. + languages: actions + config-file: ./.github/codeql/codeql-config.yml + - name: Perform CodeQL analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:actions" diff --git a/.lychee.toml b/.lychee.toml index 47a4613..7d7a58e 100644 --- a/.lychee.toml +++ b/.lychee.toml @@ -4,6 +4,9 @@ exclude = [ "^https://github.com/.*/releases/tag/v.*$", "^https://doi.org/FIXME$", "zenodo.org/badge/DOI/FIXME$", + # These sites block automated requests (return 403 to the link checker) + "^https://www\\.sciencedirect\\.com/.*$", + "^https://www\\.timeanddate\\.com/.*$", ] exclude_path = ["docs/build"] diff --git a/CLAUDE.md b/CLAUDE.md index 428c075..8981e1c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -52,13 +52,20 @@ src/ hughes.jl, bennett.jl, sg2.jl, spa.jl, ... Utilities/ # Sunrise/sunset/transit calculations srt.jl, spa.jl -ext/ # Weak dependency extensions - SolarPositionMakieExt.jl # Sun path plotting - SolarPositionOhMyThreadsExt.jl # Parallel solar position computation - SolarPositionModelingToolkitExt.jl # Symbolic models for MTK +ext/ # Weak dependency extensions (auto-load on `using` of the trigger pkg) + SolarPositionMakieExt.jl # Makie → analemmas!() sun-path plotting (PolarAxis/Axis) + SolarPositionOhMyThreadsExt.jl # OhMyThreads → solar_position[!] with an extra ::Scheduler arg + SolarPositionModelingToolkitExt.jl # ModelingToolkit/Symbolics → SolarPositionBlock() (t in SECONDS) ``` -**Core API pattern:** `solar_position(algorithm, observer, datetime)` returns a `SolPos` or `ApparentSolPos` (if refraction is included). The `Observer` struct holds location (lat/lon/altitude) and optional atmospheric parameters (pressure, temperature). +Minimum Julia: **1.10** (LTS). Extensions only activate once their trigger package is loaded; don't `import` them directly. + +**Core API pattern:** `solar_position(obs, dt, alg=PSA(), refraction=DefaultRefraction())` — observer first, then datetime, then algorithm/refraction (both default-able). `dt` may be a single `DateTime` or an `AbstractVector{DateTime}` (returns a `StructArray`). A table interface (`solar_position(table, obs; dt_col=:datetime)`) and an in-place `solar_position!(pos, obs, dts, alg, refraction)` also exist. + +- **Algorithms and refraction models are singleton dispatch types** — e.g. `PSA()`, `NOAA()`, `SPA()`, `HUGHES(pressure, temperature)`, `NoRefraction()`. New algorithms are added by defining a struct `<: SolarAlgorithm` (or `<: RefractionAlgorithm`) and a `solar_position` method on it. +- **Return type depends on refraction:** `result_type(...)` yields `SolPos{T}` for `NoRefraction`, else `ApparentSolPos{T}` (adds `apparent_elevation`/`apparent_zenith`). +- **Angle convention:** all degrees. Azimuth 0°=North, +clockwise, [-180°, 180°]; elevation [-90°, 90°]; zenith = 90° − elevation. +- `Observer(latitude, longitude; altitude=0.0, horizon=0.0)` precomputes lat/lon trig (`sin_lat`, `cos_lat`) for performance; it also holds optional pressure/temperature for refraction. **Test discovery:** Test files matching `test-*.jl` under `test/` are automatically discovered and wrapped in `@testset`. Reference values live in `expected-values.jl` files alongside algorithm tests.