| name | cicd |
| description | Use when touching .github/workflows/, release config (.releaserc*), mkdocs.yml, docs deploy, or the whats-new generator. Covers branch-to-release-channel mapping, semantic-release, docs deploy gating, and the agentic whats-new flow. |
CI/CD in pytrendy
Workflows
| File | Purpose |
|---|
test.yaml | Core tests → non-core (--cov-append) → Codecov. Runs on push to main/develop + PRs. |
release.yaml | Core tests → semantic-release → build & publish to PyPI. Runs on push to main/develop. |
docs.yaml | Build + deploy docs to GitHub Pages (gh-pages branch, per-subdirectory). main/develop. |
docs-preview.yaml | Per-PR docs preview at russellsb.github.io/pytrendy/pr-<N>/. |
whats-new.yaml | Agentic: generates docs/whats-new.md entry via OpenCode CLI, opens PR back. |
check-base-branch.yml | Fails PRs not targeting develop (with documented exceptions). |
lint-pr-title.yml | Enforces Conventional Commits on PR titles. |
codeql.yml | GitHub code scanning. |
Branch → release channel
main → stable. .releaserc config; branches: ["main"]; publishes to PyPI as 1.x.x.
develop → prerelease dev channel. Release workflow does cp .releaserc.dev.json .releaserc before running; branches includes { "name": "develop", "prerelease": "dev", "channel": "dev" }; publishes as 1.x.x.devN (the prepareCmd rewrites -dev. → .dev for PEP 440 compliance).
Version source of truth = semantic-release output, applied via poetry version ${nextRelease.version}. Never hand-edit pyproject.toml's version — it desyncs the next release.
Release flow (release.yaml)
test-core job: pytest tests/ -m core.
semantic-release: conventional-commits analyzer → bumps version → poetry version → commits CHANGELOG.md + pyproject.toml (main) or just pyproject.toml (develop) with chore(release): <ver> [skip ci]. Uses SSH deploy key (RELEASE_SSH_KEY).
build-and-publish: only if released == 'true' (or force_publish dispatch). poetry build → PyPI via OIDC trusted publishing (id-token: write, environment: release).
Docs deploy gating (docs.yaml)
The check-should-deploy job decides whether to build:
- Skip if any commit message contains
[skip docs].
- Deploy if any commit matches
^(feat|fix|docs|refactor|perf)(\([^)]*\))?!?: (semantic types).
- Deploy if any commit subject has
! (breaking) or body has BREAKING CHANGE.
- Else diff files: deploy if changes touch
docs/, mkdocs.yml, or pytrendy/.
Deploy writes to gh-pages under a per-branch subdirectory (main/, develop/) with keep_files: true so branches don't clobber each other. On main, also writes a root index.html redirecting to main/. The offline mkdocs plugin is stripped via sed for online deploys; site_url is injected per-branch.
GitHub Pages setup is a one-time manual step (Settings → Pages → gh-pages / root) — noted in docs.yaml header comment.
Docs preview (docs-preview.yaml)
Triggers on PR open/sync/reopen/close touching docs/**, mkdocs.yml, or pytrendy/**. Skips fork PRs (no gh-pages write access). Bot comments URL with stable marker <!-- docs-preview-bot --> (updates existing comment rather than duplicating). On PR close, removes pr-<N>/ from gh-pages and updates the comment.
What's New generator (whats-new.yaml) — agentic
Triggers: on release publish, on Release workflow completion (main/develop), or manual dispatch.
- Resolves release metadata (tag, name, body, prerelease flag, branch) from the event payload, falling back to the GitHub Releases API by tag, then
CHANGELOG.md.
- Checks out the release branch, installs the OpenCode CLI (see
whats-new.yaml for the exact install step).
- Runs
python scripts/generate_whats_new.py with OPENCODE_MODEL (default opencode-go/deepseek-v4-flash) and a deny-all permission block (bash/edit/webfetch/websearch/external_directory/task all deny) — the agent can only read and write the whats-new file via the script.
- The script prepends a user-friendly entry into
docs/whats-new.md between sentinel comments <!-- WHATS_NEW_CONTENT_START --> / <!-- WHATS_NEW_CONTENT_END -->. Don't edit content inside those markers by hand — it gets regenerated.
- Opens a PR (
docs/whats-new-<tag>) back to the release branch via peter-evans/create-pull-request@v7 using DOCS_PREVIEW_PAT. The PR's base is the release branch (develop or main) — these are the only PRs allowed to target main from a non-develop head (check-base-branch.yml whitelists docs/whats-new-*).
- On stable releases (main, not prerelease), a
sync-to-develop job re-generates the entry framed as stable and opens a follow-up PR to develop so both branches stay consistent.
Script-level agent instructions are in scripts/generate_whats_new.py docstring: verify CSV URLs resolve (use develop-branch raw URLs for pre-releases, main for stable), derive before/after plot scenarios from tests/tests_crashes_edgecases/ not synthetic data, and keep figsize/grid/legend/colors identical between before/after images by routing through the same detect_trends() + plot_pytrendy() pipeline.
For manual before/after plot generation in PR bodies (fix/feature PRs), see the pr-plots skill.
Secrets the workflows expect
CODECOV_TOKEN, RELEASE_SSH_KEY, OPENCODE_API_KEY, DOCS_PREVIEW_PAT, plus GITHUB_TOKEN. OPENCODE_MODEL is a repo variable (vars context), not a secret. PyPI publish uses OIDC trusted publishing (no token).
OpenCode App installation scopes
The OpenCode GitHub App (opencode-agent) requires specific repository permissions for commits, issues, and PRs. Verify in GitHub Settings → Integrations → OpenCode App → Installations:
- Contents: read/write (for commits)
- Issues: read/write (for creating and commenting on issues)
- Pull requests: read/write (for creating and commenting on PRs)
Fallback (user awareness only — do not attempt to apply): If App scopes cannot be fixed, the user can set use_github_token: true in .github/workflows/opencode.yml (backed by the widened workflow permissions: block). This requires changes to the workflow on main, so the agent should not attempt to work around it.