Skip to content

ci: upgrade tox to 4.x, target Python 3.13, document local test workflow#573

Open
lgelauff wants to merge 24 commits into
masterfrom
tools/tox-py313
Open

ci: upgrade tox to 4.x, target Python 3.13, document local test workflow#573
lgelauff wants to merge 24 commits into
masterfrom
tools/tox-py313

Conversation

@lgelauff
Copy link
Copy Markdown
Collaborator

Depends on #564 — must merge tools/buildservice first; this branch is based on it.

Summary

  • Upgrades tox from 3.x to 4.x and targets Python 3.13 (matching production on Toolforge)
  • Sets skip_missing_interpreters = false so tox fails loudly if Python 3.13 is not installed
  • Drops stale tox 3.x pins (tox<3.15, pluggy<1) and unused dev deps (Fabric3, coverage==5.0.2)
  • Documents tox as the recommended local test command in dev.md, with the Docker path retained for exact CI reproduction

Why tox

The Toolforge buildservice cannot be run locally. tox fills the reproducibility gap by creating a clean, isolated Python 3.13 environment for every local test run — independent of whatever venv the developer happens to have set up.

Test plan

🤖 Generated with Claude Code

lgelauff and others added 24 commits May 4, 2026 15:47
Replace toolforge webservice python3.13 with the Toolforge buildservice
(Cloud Native Buildpacks). Adds Procfile, .python-version, and a root-level
package.json shim so both Python and Node buildpacks activate, baking the
Vue frontend into the container image and eliminating the separate node20
frontend-build job.

Config and env detection are updated in utils.py to work inside containers:
- get_env_name() checks MONTAGE_ENV first (LDAP/getpass.getuser unavailable
  in containers per Toolforge docs)
- load_env_config() loads secrets from individual MONTAGE_* environment
  variables (stored via toolforge envvars) when MONTAGE_OAUTH_CONSUMER_TOKEN
  is set; falls back to YAML for local dev

Removes the urllib3/pyopenssl SSL shim (deprecated since urllib3 2.0, no
longer needed on Python 3.13) and its dependency from requirements.in.
Removes the cryptography version pin, which was a uwsgi+PyO3 workaround
that does not apply to gunicorn. Adds gunicorn to requirements.in.

Rewrites deployment.md for the buildservice workflow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Matches the Python version used on Toolforge (.python-version). Required
by gunicorn>=20 which dropped support for Python <3.10.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
cffi 1.16.0 ships a cp39 manylinux wheel but not a cp313 one, so pip
falls back to compiling from source on Python 3.13. The slim image has
no C toolchain. gcc and libffi-dev are the standard build requirements
for cffi. The underlying fix (cffi>=1.17.1, which has cp313 wheels) is
tracked in #512.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
cffi 1.17.1 adds cp313 wheels, eliminating source compilation on Python
3.13 (and the need for gcc in the CI image). setuptools is pinned <82
because setuptools 82 removed pkg_resources, which python-graph-core
depends on via namespace packages. Remove once python-graph-core is
replaced (#421).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Heroku Node.js buildpack v5 requires a package-lock.json to run
npm ci and npm run build. Without it, the buildpack installs Node.js
but skips the install/build steps, leaving montage/static/ empty.

The build script runs: cd frontend && npm ci && npm run toolforge:build
which builds the Vite frontend and copies assets to montage/static/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Toolforge buildservice startup probe checks port 8000. The earlier
change to 5000 was incorrect and caused the startup probe to fail with
"connection refused", putting pods into CrashLoopBackOff.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
NFS must be mounted so Montage can write logs to /data/project/<toolname>/.
Also adds URLs to the multi-environment table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
frontend/.env is gitignored, so the buildpack never had it. Without it,
VITE_API_ENDPOINT was undefined at build time, making every Axios call go
to /undefined/v1/... and fail with 404. .env.production sets it to empty
string so Vite produces a relative /v1/ base URL, correct for same-origin
Toolforge deployments.

Also adds build verification and debugging guidance to deployment.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
**Important for deployers**: Montage now uses OAuth 2.0. If you have an
existing OAuth 1.0a consumer registered on Meta-Wiki, you will need to
register a new OAuth 2.0 consumer at Special:OAuthConsumerRegistration.

Changes:
- Replace mwoauth Handshaker/RequestToken with direct OAuth 2.0 PKCE flow
  using requests against rest.php/oauth2/ endpoints
- New env vars: MONTAGE_OAUTH_CLIENT_ID, MONTAGE_OAUTH_CLIENT_SECRET,
  MONTAGE_OAUTH_REDIRECT_URI (replaces MONTAGE_OAUTH_CONSUMER_TOKEN /
  MONTAGE_OAUTH_SECRET_TOKEN)
- Remove mwoauth, oauthlib, requests-oauthlib, pyjwt from dependencies
- debug mode in complete_login now reads debug_username/debug_userid
  from config instead of hardcoded values

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The juror round listing was filtering on campaign status only, so
finalized and cancelled rounds inside active campaigns still appeared.
Jurors clicking them got a confusing 400 "round not active" error.

Now only active and paused rounds are shown in the active view.

Suspected cause: e3069dd (PR #350, merged 2025-10-23) added a
finalize_round endpoint that can finalize individual rounds without
advancing, creating a state the juror listing never filtered for.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Automates: build → poll for completion → verify SHA + port →
warn if running SHA already matches → restart → smoke-test /meta/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- git pull --ff-only at start so the script stays current
- default ref changed to master (was tools/buildservice)
- document one-time clone in script header

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Paused rounds were still appearing in the juror round list because
the only_active filter allowed both ACTIVE_STATUS and PAUSED_STATUS.
However confirm_active() only permits ACTIVE_STATUS, so clicking a
paused round always produced a 400 "round not active" error.

Fix restricts the juror index to strictly active rounds only.

Suspected introduced by e3069dd (#350).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _load_config_from_env now raises ValueError if MONTAGE_DEBUG=true
  in prod, beta, or devlabs; prevents a single envvar command from
  disabling auth on a live Toolforge tool
- UserMiddleware auto-login path split: devtest keeps the test fixture
  user (6024474); debug mode uses userid=0 / '__montage_debug__' which
  has no match in any real DB, producing an immediate and obvious error
  if debug mode somehow reaches a production database
- complete_login debug defaults changed to the same sentinel values so
  config.dev.yaml must explicitly set debug_userid / debug_username

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Unvalidated ?next= parameter allowed open redirects: any URL passed
to /login?next= or /logout?next= was stored in a signed cookie and
redirected to after auth, enabling phishing via crafted links.

_safe_next() accepts only same-origin paths (starts with / but not //)
and falls back to root_path for anything else. Applied at all three
sites: login (cookie store), logout (direct redirect), and complete_login
(cookie read, defence-in-depth).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
get_index() accepted an only_active parameter but hard-coded True
when calling get_all_rounds_task_counts, so get_all_campaigns()
(the archive endpoint at /juror/campaigns/all) was always returning
an empty list instead of finalized rounds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously scm_secure was only True when env_name == 'prod', so
beta and devlabs deployments (both served over HTTPS on Toolforge)
shipped session cookies without the Secure flag. A MONTAGE_ENV typo
(e.g. 'production' instead of 'prod') would silently drop it on prod.

- scm_secure is now True for all envs except dev and devtest
- app startup raises on unknown env names so typos fail loudly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tests

- LoggingMiddleware now redacts code/state/access_token/token from
  request.args before writing to api_log; these are auth material and
  should never appear on disk
- complete_login no longer swallows exceptions silently; OAuth exchange
  failures are now logged via api_log so operators can distinguish
  'user cancelled' from 'client secret is wrong' or 'MW is down'
- Four new tests cover the PKCE flow using mocked MW endpoints:
  login redirect params, state mismatch, token exchange failure,
  and successful login with cookie verification

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- app.py: refuse to start if cookie_secret is the default placeholder
  or shorter than 32 chars
- public_endpoints.py: use hmac.compare_digest for state comparison
  (constant-time, prevents timing oracle on the CSRF token)
- utils.py: print effective superuser list at startup so
  misconfigured MONTAGE_SUPERUSERS is immediately visible in logs
- deploy.sh: fail explicitly when git ls-remote cannot resolve the ref
  instead of silently using the literal branch name as the SHA
- dev.md: update OAuth keys to oauth_client_id/secret/redirect_uri
  and note that debug_userid/debug_username must be set for a useful
  local dev session

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Update tox.ini to py313, tox 4 style, skip_missing_interpreters=false
- Drop stale tox 3.x pins (tox<3.15, pluggy<1), Fabric3, coverage==5.0.2
- Document tox as the recommended local test command in dev.md
- Keep Docker path in dev.md for exact CI reproduction

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant