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
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:
github-actions:
patterns: ["*"]
cooldown:
default-days: 5
- package-ecosystem: uv
directory: /
schedule:
interval: weekly
groups:
python:
patterns: ["*"]
cooldown:
default-days: 5
semver-major-days: 14
38 changes: 38 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Publish

# Disabled by default. Publishing only runs once the repository variable
# PUBLISH_ENABLED is set to "true":
# gh variable set PUBLISH_ENABLED --body true # enable
# gh variable set PUBLISH_ENABLED --body false # disable (or `gh variable delete`)
# Until then, tag pushes trigger this workflow but both jobs skip.
on:
push:
tags: ["v*"]

jobs:
build:
if: vars.PUBLISH_ENABLED == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.3
- uses: astral-sh/setup-uv@v8.2.0
- run: uv build
- uses: actions/upload-artifact@v7.0.1
with:
name: dist
path: dist/

publish:
needs: build
if: vars.PUBLISH_ENABLED == 'true'
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
contents: read
steps:
- uses: actions/download-artifact@v8.0.1
with:
name: dist
path: dist/
- uses: pypa/gh-action-pypi-publish@v1.14.0
32 changes: 32 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Tests

on:
push:
branches: [dev, main]
pull_request:
branches: [dev, main]

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13", "3.14"]
# UV_PYTHON outranks the .python-version pin for uv sync and every uv run,
# so each matrix cell actually tests its own interpreter. Without it,
# bare `uv run` re-resolves to .python-version and every cell silently
# runs the same Python.
env:
UV_PYTHON: ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v6.0.3
- uses: astral-sh/setup-uv@v8.2.0
- run: uv python install ${{ matrix.python-version }}
- run: uv sync --all-groups
- run: uv run ruff check .
- run: uv run ruff format --check .
- run: uv run pyrefly check
- run: uv run pytest --cov --cov-report=term-missing
25 changes: 24 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,25 @@
.archive/
.claude/
.hypothesis/
.pytest_cache/
.ruff_cache/
.venv/
.vscode/
.worktrees/

__pycache__/
build/
dist/
*.egg-info/

.DS_Store

.coverage
.coverage.*

.env
.env.*
!.env.example

data
notebooks
notebooks
5 changes: 5 additions & 0 deletions .planners/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Plans

| # | Plan | Status | Concluded | PR |
|---|---|---|---|---|
| 000 | [Upgrade repo to the proj-template standard](plans/000-template-upgrade/plan.md) | done | 2026-07-24 09:03 PT | [#1](https://github.com/gitronald/wayback/pull/1) |
106 changes: 106 additions & 0 deletions .planners/plans/000-template-upgrade/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
id: 0
slug: template-upgrade
status: done
branch: feature/template-upgrade
created: 2026-07-24T08:00:07-07:00
concluded: 2026-07-24T09:03:43-07:00
pr: https://github.com/gitronald/wayback/pull/1
---

# Upgrade repo to the proj-template standard

## Plan

Bring this repo up to the current proj-template standard (uv, ruff, pyrefly,
pre-commit, Claude hooks, GitHub Actions, planners, stanza) via the
install-template skill, Mode 2 (upgrade an existing repo).

### Classification

**package** — setuptools-based `setup.py`, an importable `wayback/` package
dir, installed via pip. No `pyproject.toml`, no tests, no CI, no `.planners/`
prior to this plan.

### Sync decisions (per matrix)

- `pyproject.toml` — create from the template (none exists), migrating
metadata from `setup.py`: name, description, authors, and runtime deps
(`waybackpy`, `requests`, `pandas`, `tqdm`). Hatchling build backend, sdist
`only-include`, `[project.urls]`, ruff + pyrefly (strict) + pytest config,
dev group. Version set to `0.1.0a0` (dev-branch alpha; `0.0.1` was never
tagged or released). No `[project.scripts]` — the package has no CLI.
`setup.py` is removed once `pyproject.toml` replaces it.
- `.pre-commit-config.yaml` — copy from template (ruff format/lint, pyrefly,
planners-validate). No existing local hooks to preserve.
- `.python-version` — create at 3.14.
- `.gitignore` — merge template entries; keep repo-specific `data` and
`notebooks` entries.
- `.claude/settings.json`, `.claude/hooks/lint-typecheck.sh`, `.claude/CLAUDE.md`
— copy from template (none exist; CLAUDE.md adapted to this package).
On-disk only: `.claude/` is gitignored per the template default.
- `.github/workflows/test.yml` — sync: full Python 3.11–3.14 matrix with the
job-level `UV_PYTHON` pin.
- `.github/workflows/publish.yml` — sync (disabled by default behind the
`PUBLISH_ENABLED` repo variable).
- `.github/dependabot.yml` — create with grouped weekly updates + cooldown for
`github-actions` and `uv` ecosystems. Repo toggles: Dependabot alerts on,
automated security fixes off.
- `.planners/` — scaffolded by this plan.
- `tests/` — none existed; add a minimal smoke test (import + public API
presence, no network) so the pytest dev-group deps and the CI pytest step
have something real to run. The package's own code is otherwise untouched
except for lint/type fixes required by the verification gate.

### Deliberately skipped

- `README.md`, `CHANGELOG.md`, `LICENSE`, package source content — never
synced per the matrix (lint/type fixes at the source excepted).
- Renovate variant — repo uses the Dependabot default.

## Log

- 2026-07-24: Applied the full sync matrix. Migrated `setup.py` to
`pyproject.toml` (hatchling, version `0.1.0a0`, metadata and deps carried
over) and removed `setup.py`. Copied pre-commit config, `.python-version`
(3.14), CI workflows, dependabot config, and the `.claude/` payload
(on-disk only — `.claude/` is gitignored).
- 2026-07-24: Verification green: `uv sync --all-groups`, ruff check + format,
pyrefly (strict, 0 errors), `pre-commit run --all-files`, pytest (1 smoke
test). Source fixes needed: `__all__` re-exports in `__init__.py`, wrapped
one over-long docstring line, `list` → `list[str]`, and
`output_directory: str = None` → `str | None = None`.
- 2026-07-24: Deviation from the "never touch README" row: ruff 0.16 formats
Python code blocks inside markdown, so `ruff format --check .` (CI and
pre-commit) requires a one-line quote-style normalization in the README's
example block. Accepted it rather than excluding README from the formatter,
since the template standard runs ruff with defaults.
- 2026-07-24: Repo security toggles set: Dependabot alerts enabled, automated
security fixes confirmed off.
- 2026-07-24: Close-out review gate on PR #1. Review follow-up: one actionable
finding — `.pre-commit-config.yaml` pinned ruff-pre-commit v0.15.14 while
`uv.lock` resolves ruff 0.16.0, a skew that could let a commit pass the hook
but fail CI's `ruff format --check` (the same 0.15 -> 0.16 markdown-formatting
change already logged above). Bumped the hook rev to v0.16.0; full gate rerun
green including `pre-commit run --all-files` with the new rev. Conscious
no-ops (pre-existing source behavior, untouched per matrix): import-time
`end_timestamp` default, `assert`-based required-arg check, broad
`except Exception` in the URL loop, no top-level `permissions:` in
publish.yml. Review posted to the PR.

## Retrospective

- The sync matrix held up: every template file mapped cleanly onto the repo,
and the only judgment calls were the ones the plan pre-decided (version
`0.1.0a0`, no `[project.scripts]`, smoke test to give CI something real).
- Tool-version skew was the recurring theme: ruff 0.16's markdown formatting
forced the one README deviation, and the same 0.15/0.16 split resurfaced at
close as a pre-commit rev behind the lockfile. When a template pins a hook
rev, check it against the resolved lock during the upgrade, not just at
review.
- Annotating legacy signatures (`str | None`) was safe only because a
pre-existing runtime guard already narrowed the value — worth confirming the
guard exists before trusting a type-only fix to strict pyrefly.
- A no-network smoke test was enough to make the whole CI pipeline meaningful
on day one; behavioral tests can come later without blocking the template
work.
23 changes: 23 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.16.0
hooks:
- id: ruff-format
- id: ruff
args: [--fix]
- repo: local
hooks:
- id: pyrefly-check
name: pyrefly check
entry: uv run pyrefly check
language: system
types_or: [python, pyi]
pass_filenames: false
require_serial: true
- repo: local
hooks:
- id: planners-validate
name: validate plan frontmatter
entry: planners validate
language: system
files: ^\.planners/plans/[^/]+/plan\.md$
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.14
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.1.0] - 2026-07-24

### Added

- `get_url_snapshots`: collect Wayback Machine CDX snapshots for a URL into a DataFrame
- `get_url_list_snapshots`: batch-collect snapshots for a list of URLs into per-URL CSV files
- Packaging via `pyproject.toml` (hatchling build backend, uv-managed), replacing `setup.py`
- Test suite and GitHub Actions CI: lint, type check, and tests on Python 3.11-3.14
- Pre-commit hooks (ruff format + lint, pyrefly, planners validation)
- PyPI publish workflow, disabled behind the `PUBLISH_ENABLED` repository variable
- Dependabot with grouped weekly updates

[Unreleased]: https://github.com/gitronald/wayback/compare/v0.1.0...HEAD
[0.1.0]: https://github.com/gitronald/wayback/releases/tag/v0.1.0
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ data = wayback.get_url_snapshots(
end_timestamp="20231231",
)
print(f"snapshots found: {len(data):,}")
show_cols = ['url', 'statuscode', 'archive_url', 'timestamp']
show_cols = ["url", "statuscode", "archive_url", "timestamp"]
print(data[show_cols].head(5).to_markdown())
```

Expand All @@ -32,7 +32,7 @@ processing lazerlab.net
extracting items
reshaping items
snapshots found: 207
``````
```

| url | statuscode | archive_url | timestamp |
|:-------------|-------------:|:-----------------------------------------------------------------------|---------------:|
Expand Down
72 changes: 72 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
[project]
name = "wayback"
version = "0.1.0"
description = "A wrapper for collecting data from the Wayback Machine"
authors = [
{name = "Ronald E. Robertson", email = "rer@acm.org"}
]
requires-python = ">=3.11"
dependencies = [
"waybackpy",
"requests",
"pandas",
"tqdm",
]
readme = "README.md"

[project.urls]
repository = "https://github.com/gitronald/wayback"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.sdist]
# Ship only what a PyPI consumer needs. Root-anchored (leading "/") so bare
# filenames don't glob at every depth — this keeps internal plan files
# (.planners/), the .claude/ holder, tests, and tooling out of the
# published sdist.
only-include = [
"/wayback",
"/README.md",
"/LICENSE",
]

[dependency-groups]
dev = [
"pre-commit",
"pyrefly>=1.0.0",
"pytest>=9.0.2",
"pytest-cov",
"ruff>=0.15",
]

[tool.ruff]
target-version = "py311"

[tool.ruff.lint]
select = [
"F", # pyflakes
"E", # pycodestyle errors
"W", # pycodestyle warnings
"I", # isort (import sorting)
"UP", # pyupgrade (modern Python idioms)
]

[tool.pyrefly]
preset = "strict"
python-version = "3.11"
project-includes = ["."]
project-excludes = ["**/__pycache__", "**/.venv"]

# tests/ stays type-checked (imports, undefined names, type mismatches),
# but the implicit-any check is relaxed there so unannotated pytest fixture
# params (e.g. `def test_x(tmp_path):`) don't force annotations.
[[tool.pyrefly.sub-config]]
matches = "tests/**"

[tool.pyrefly.sub-config.errors]
implicit-any = false

[tool.pytest.ini_options]
testpaths = ["tests"]
18 changes: 0 additions & 18 deletions setup.py

This file was deleted.

Empty file added tests/__init__.py
Empty file.
Loading