Skip to content

Commit e3bb624

Browse files
committed
Add CI + auto-release workflows; publish-ready pyproject metadata
CI (.github/workflows/ci.yml) - Matrix: ubuntu-latest + windows-latest × Python 3.12 / 3.13 / 3.14 - Steps: ruff check, bandit -c pyproject.toml -r autopapertoppt sources, pytest -q - Concurrency-cancels stale runs on the same branch to save minutes Release (.github/workflows/release.yml) - Trigger: push to main (i.e. PR merge) - Detects whether pyproject.toml's version line changed in this push; skips the job entirely when the version was not bumped - When bumped: build sdist + wheel, twine check, twine upload via the PYPI_API_TOKEN secret, then softprops/action-gh-release tags v<version> with auto-generated notes - Bound to a `pypi` GitHub Environment so the deploy is auditable and can optionally be gated by manual approval Publish-ready metadata in pyproject.toml - license = LICENSE, authors, keywords, classifiers (3.12/3.13/3.14, MIT, Science/Research). Verified locally with `python -m build` + `twine check dist/*` — both sdist and wheel pass. README - CI / Release / PyPI / Python-versions / License badges at the top - New "Continuous integration & releases" section documenting the PYPI_API_TOKEN secret setup, the bump-version-then-merge release flow, and the optional `pypi` Environment for manual approval
1 parent 07dcc58 commit e3bb624

4 files changed

Lines changed: 256 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: ["**"]
6+
pull_request:
7+
branches: [main]
8+
9+
# Cancel previous in-progress runs for the same branch to save CI minutes.
10+
concurrency:
11+
group: ci-${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
14+
jobs:
15+
test:
16+
name: ${{ matrix.os }} · Python ${{ matrix.python-version }}
17+
runs-on: ${{ matrix.os }}
18+
strategy:
19+
# Don't abort the whole matrix when one cell fails — surfaces all
20+
# OS/version regressions in a single CI run.
21+
fail-fast: false
22+
matrix:
23+
os: [ubuntu-latest, windows-latest]
24+
python-version: ["3.12", "3.13", "3.14"]
25+
26+
steps:
27+
- name: Checkout
28+
uses: actions/checkout@v4
29+
30+
- name: Set up Python ${{ matrix.python-version }}
31+
uses: actions/setup-python@v5
32+
with:
33+
python-version: ${{ matrix.python-version }}
34+
# Cache pip downloads keyed on requirements files so re-runs
35+
# for the same dep set skip the wheel build.
36+
cache: pip
37+
cache-dependency-path: |
38+
pyproject.toml
39+
requirements.txt
40+
requirements-dev.txt
41+
42+
- name: Install dev extras
43+
run: |
44+
python -m pip install --upgrade pip
45+
pip install -e ".[dev]"
46+
47+
- name: ruff lint
48+
run: python -m ruff check .
49+
50+
- name: bandit security scan
51+
# The -c flag is mandatory — without it bandit ignores the
52+
# project's per-rule skip configuration in pyproject.toml.
53+
run: python -m bandit -c pyproject.toml -r autopapertoppt/ sources/
54+
55+
- name: pytest
56+
# Hermetic fixtures only — no live HTTP. -X utf8 keeps Windows
57+
# console codec out of the picture for paper titles containing
58+
# accented or CJK characters.
59+
run: python -X utf8 -m pytest tests/ -q

.github/workflows/release.yml

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
name: Release to PyPI
2+
3+
# Triggered only when commits land on main (i.e. a PR was merged or a
4+
# direct push happened). The job is a no-op unless pyproject.toml's
5+
# version actually changed in this push, so re-running CI on main
6+
# without bumping the version is free.
7+
on:
8+
push:
9+
branches: [main]
10+
11+
# Avoid duplicate concurrent releases for the same ref.
12+
concurrency:
13+
group: release-main
14+
cancel-in-progress: false
15+
16+
jobs:
17+
detect-version:
18+
name: Detect version change
19+
runs-on: ubuntu-latest
20+
outputs:
21+
version: ${{ steps.read.outputs.version }}
22+
changed: ${{ steps.compare.outputs.changed }}
23+
steps:
24+
- name: Checkout (with history)
25+
uses: actions/checkout@v4
26+
with:
27+
# Need at least two commits of history to diff pyproject.toml
28+
# against the previous state — depth 2 covers the normal case
29+
# of one merge commit landing.
30+
fetch-depth: 2
31+
32+
- name: Read current version
33+
id: read
34+
shell: bash
35+
run: |
36+
VERSION=$(grep -E '^version *= *"' pyproject.toml \
37+
| head -1 \
38+
| sed -E 's/.*"([^"]+)".*/\1/')
39+
if [ -z "$VERSION" ]; then
40+
echo "::error::could not read version from pyproject.toml"
41+
exit 1
42+
fi
43+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
44+
echo "current pyproject.toml version: $VERSION"
45+
46+
- name: Compare to previous commit
47+
id: compare
48+
shell: bash
49+
run: |
50+
set -euo pipefail
51+
# If pyproject.toml didn't change at all between HEAD and HEAD~1,
52+
# this push didn't touch the version. Skip.
53+
if git diff --quiet HEAD~1 HEAD -- pyproject.toml; then
54+
echo "pyproject.toml unchanged in this push — skipping release"
55+
echo "changed=false" >> "$GITHUB_OUTPUT"
56+
exit 0
57+
fi
58+
PREV=$(git show HEAD~1:pyproject.toml \
59+
| grep -E '^version *= *"' \
60+
| head -1 \
61+
| sed -E 's/.*"([^"]+)".*/\1/' \
62+
|| echo "")
63+
NEW="${{ steps.read.outputs.version }}"
64+
echo "previous version: ${PREV:-<none>}"
65+
echo "current version: $NEW"
66+
if [ "$PREV" = "$NEW" ]; then
67+
echo "version line edited but value unchanged — skipping release"
68+
echo "changed=false" >> "$GITHUB_OUTPUT"
69+
else
70+
echo "version bumped $PREV → $NEW — proceeding to release"
71+
echo "changed=true" >> "$GITHUB_OUTPUT"
72+
fi
73+
74+
publish:
75+
name: Build + publish to PyPI
76+
needs: detect-version
77+
if: needs.detect-version.outputs.changed == 'true'
78+
runs-on: ubuntu-latest
79+
# Bind the deployment to a GitHub Environment so the secret access
80+
# is auditable and the release shows up in the repo's Deployments
81+
# tab. The environment also lets reviewers gate the deploy with a
82+
# manual approval if they want to (configure in repo settings).
83+
environment:
84+
name: pypi
85+
url: https://pypi.org/project/autopapertoppt/${{ needs.detect-version.outputs.version }}/
86+
steps:
87+
- name: Checkout
88+
uses: actions/checkout@v4
89+
90+
- name: Set up Python
91+
uses: actions/setup-python@v5
92+
with:
93+
python-version: "3.12"
94+
cache: pip
95+
cache-dependency-path: pyproject.toml
96+
97+
- name: Install build backend
98+
run: |
99+
python -m pip install --upgrade pip
100+
pip install build twine
101+
102+
- name: Build sdist + wheel
103+
run: python -m build
104+
105+
- name: Verify distribution metadata
106+
# `twine check` catches the common publish-time failures:
107+
# malformed long description, missing license, wrong classifiers.
108+
run: python -m twine check dist/*
109+
110+
- name: Publish to PyPI
111+
env:
112+
TWINE_USERNAME: __token__
113+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
114+
run: python -m twine upload --non-interactive dist/*
115+
116+
- name: Create GitHub release
117+
# Auto-generated release notes from the merged PRs. Tagged
118+
# v<version> so the GitHub release UI links back to the PyPI
119+
# publish above.
120+
uses: softprops/action-gh-release@v2
121+
with:
122+
tag_name: v${{ needs.detect-version.outputs.version }}
123+
name: v${{ needs.detect-version.outputs.version }}
124+
generate_release_notes: true
125+
# The tag is created at the current HEAD. The job only runs
126+
# when the version was bumped, so HEAD is the merge commit
127+
# the user wants to tag.
128+
target_commitish: ${{ github.sha }}
129+
env:
130+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# AutoPaperToPPT
22

3+
<!--
4+
Badges below use placeholder `OWNER/REPO` and `autopapertoppt` (PyPI
5+
package name). After you push this repo to GitHub, replace `OWNER/REPO`
6+
with your actual GitHub `<user>/<repo>` so the badges resolve.
7+
-->
8+
9+
[![CI](https://github.com/OWNER/REPO/actions/workflows/ci.yml/badge.svg)](https://github.com/OWNER/REPO/actions/workflows/ci.yml)
10+
[![Release](https://github.com/OWNER/REPO/actions/workflows/release.yml/badge.svg?branch=main)](https://github.com/OWNER/REPO/actions/workflows/release.yml)
11+
[![PyPI](https://img.shields.io/pypi/v/autopapertoppt.svg)](https://pypi.org/project/autopapertoppt/)
12+
[![Python](https://img.shields.io/pypi/pyversions/autopapertoppt.svg)](https://pypi.org/project/autopapertoppt/)
13+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
14+
315
> **Languages**: **English** · [繁體中文](README.zh-TW.md) · [简体中文](README.zh-CN.md) · [日本語](README.ja.md) · [Español](README.es.md) · [Français](README.fr.md) · [Deutsch](README.de.md) · [한국어](README.ko.md) · [Português](README.pt.md) · [Русский](README.ru.md) · [Italiano](README.it.md) · [Tiếng Việt](README.vi.md) · [हिन्दी](README.hi.md) · [Bahasa Indonesia](README.id.md)
416
> **Documentation**: [Read the Docs source](docs/) (Sphinx)
517
@@ -375,6 +387,36 @@ The `-c` flag on bandit is required — without it bandit ignores the
375387
project skip config. When touching the pptx exporter, also run an
376388
overflow check (see `CLAUDE.md` "Slide Deck Rules").
377389

390+
## Continuous integration & releases
391+
392+
Two GitHub Actions workflows live under `.github/workflows/`:
393+
394+
- **`ci.yml`** runs on every push and PR to `main`. Matrix is Ubuntu +
395+
Windows × Python 3.12 / 3.13 / 3.14 (6 jobs). Each job runs
396+
`ruff check`, `bandit -c pyproject.toml`, and `pytest`.
397+
- **`release.yml`** runs on every push to `main`. It diffs
398+
`pyproject.toml` against the previous commit; if the `version =
399+
"..."` line changed, it builds an sdist + wheel, verifies them with
400+
`twine check`, uploads to PyPI, and creates a tagged GitHub release
401+
with auto-generated notes. If the version did **not** change, the
402+
job exits early — most merges to `main` are no-ops.
403+
404+
To enable PyPI publishing:
405+
406+
1. Generate a project-scoped API token at
407+
<https://pypi.org/manage/account/token/>.
408+
2. In the GitHub repo: `Settings → Secrets and variables → Actions →
409+
New repository secret`. Name it `PYPI_API_TOKEN` and paste the
410+
token value.
411+
3. To cut a release: bump `version` in `pyproject.toml` (e.g.
412+
`0.1.0``0.1.1`), open a PR, and merge it. The merge commit
413+
triggers `release.yml`, which uploads the new version and tags
414+
it `v<version>` on GitHub.
415+
416+
A protected `pypi` GitHub Environment is referenced by the publish
417+
step; create it under `Settings → Environments` if you want to require
418+
manual approval before each release (optional).
419+
378420
## License
379421

380422
See `LICENSE`. The arXiv API is used under arXiv's API terms of use

pyproject.toml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,31 @@ version = "0.1.0"
88
description = "Keyword-driven paper search assistant that exports slides, summaries, and BibTeX."
99
requires-python = ">=3.12"
1010
readme = { file = "README.md", content-type = "text/markdown" }
11+
license = { file = "LICENSE" }
12+
authors = [{ name = "JeffreyChen" }]
13+
keywords = [
14+
"academic",
15+
"papers",
16+
"arxiv",
17+
"powerpoint",
18+
"bibtex",
19+
"mcp",
20+
"thesis",
21+
"research",
22+
]
23+
classifiers = [
24+
"Development Status :: 4 - Beta",
25+
"Intended Audience :: Science/Research",
26+
"License :: OSI Approved :: MIT License",
27+
"Operating System :: OS Independent",
28+
"Programming Language :: Python :: 3",
29+
"Programming Language :: Python :: 3.12",
30+
"Programming Language :: Python :: 3.13",
31+
"Programming Language :: Python :: 3.14",
32+
"Topic :: Scientific/Engineering",
33+
"Topic :: Office/Business",
34+
"Topic :: Text Processing :: Markup",
35+
]
1136
dependencies = [
1237
"httpx>=0.27",
1338
"pydantic>=2.7",

0 commit comments

Comments
 (0)