ci: scripts + format

This commit is contained in:
Barrett Ruth 2026-03-04 13:51:56 -05:00
parent f2e312f860
commit 8c8e49d75c
Signed by: barrett
GPG key ID: A6C96C9349D2FC81
9 changed files with 1279 additions and 35 deletions

View file

@ -7,8 +7,13 @@
"globals": ["vim"]
},
"workspace": {
"library": ["$VIMRUNTIME/lua", "${3rd}/luv/library", "${3rd}/busted/library"],
"checkThirdParty": false
"library": [
"$VIMRUNTIME/lua",
"${3rd}/luv/library",
"${3rd}/busted/library"
],
"checkThirdParty": false,
"ignoreDir": [".direnv"]
},
"completion": {
"callSnippet": "Replace"

1
.styluaignore Normal file
View file

@ -0,0 +1 @@
.direnv/

View file

@ -26,10 +26,46 @@
ps.requests
]);
mkSubmitEnv =
pkgs:
pkgs.buildFHSEnv {
name = "cp-nvim-submit";
targetPkgs =
pkgs: with pkgs; [
uv
alsa-lib
at-spi2-atk
cairo
cups
dbus
fontconfig
freetype
gdk-pixbuf
glib
gtk3
libdrm
libGL
libxkbcommon
mesa
nspr
nss
pango
xorg.libX11
xorg.libXcomposite
xorg.libXdamage
xorg.libXext
xorg.libXfixes
xorg.libXrandr
xorg.libxcb
];
runScript = "${pkgs.uv}/bin/uv";
};
mkPlugin =
pkgs:
let
pythonEnv = mkPythonEnv pkgs;
submitEnv = mkSubmitEnv pkgs;
in
pkgs.vimUtils.buildVimPlugin {
pname = "cp-nvim";
@ -39,12 +75,15 @@
substituteInPlace lua/cp/utils.lua \
--replace-fail "local _nix_python = nil" \
"local _nix_python = '${pythonEnv.interpreter}'"
substituteInPlace lua/cp/utils.lua \
--replace-fail "local _nix_submit_cmd = nil" \
"local _nix_submit_cmd = '${submitEnv}/bin/cp-nvim-submit'"
'';
nvimSkipModule = [
"cp.pickers.telescope"
"cp.version"
];
passthru = { inherit pythonEnv; };
passthru = { inherit pythonEnv submitEnv; };
meta.description = "Competitive programming plugin for Neovim";
};
in
@ -58,6 +97,7 @@
packages = eachSystem (system: {
default = mkPlugin (pkgsFor system);
pythonEnv = mkPythonEnv (pkgsFor system);
submitEnv = mkSubmitEnv (pkgsFor system);
});
devShells = eachSystem (system: {

View file

@ -45,7 +45,12 @@ local function run_scraper(platform, subcommand, args, opts)
end
local plugin_path = utils.get_plugin_path()
local cmd = utils.get_python_cmd(platform, plugin_path)
local cmd
if subcommand == 'submit' then
cmd = utils.get_python_submit_cmd(platform, plugin_path)
else
cmd = utils.get_python_cmd(platform, plugin_path)
end
vim.list_extend(cmd, { subcommand })
vim.list_extend(cmd, args)
@ -62,6 +67,10 @@ local function run_scraper(platform, subcommand, args, opts)
end
end
if subcommand == 'submit' and utils.is_nix_build() then
env.UV_PROJECT_ENVIRONMENT = vim.fn.stdpath('cache') .. '/cp-nvim/submit-env'
end
if opts and opts.ndjson then
local uv = vim.uv
local stdout = uv.new_pipe(false)
@ -131,7 +140,12 @@ local function run_scraper(platform, subcommand, args, opts)
return
end
local sysopts = { text = true, timeout = 30000, env = env, cwd = plugin_path }
local sysopts = {
text = true,
timeout = (subcommand == 'submit') and 120000 or 30000,
env = env,
cwd = plugin_path,
}
if opts and opts.stdin then
sysopts.stdin = opts.stdin
end

View file

@ -3,6 +3,7 @@ local M = {}
local logger = require('cp.log')
local _nix_python = nil
local _nix_submit_cmd = nil
local _nix_discovered = false
local uname = vim.uv.os_uname()
@ -111,6 +112,16 @@ function M.get_python_cmd(module, plugin_path)
return { 'uv', 'run', '--directory', plugin_path, '-m', 'scrapers.' .. module }
end
---@param module string
---@param plugin_path string
---@return string[]
function M.get_python_submit_cmd(module, plugin_path)
if _nix_submit_cmd then
return { _nix_submit_cmd, 'run', '--directory', plugin_path, '-m', 'scrapers.' .. module }
end
return M.get_python_cmd(module, plugin_path)
end
local python_env_setup = false
---@return boolean

View file

@ -7,6 +7,7 @@ requires-python = ">=3.11"
dependencies = [
"backoff>=2.2.1",
"beautifulsoup4>=4.13.5",
"camoufox[geoip]",
"curl-cffi>=0.13.0",
"httpx>=0.28.1",
"ndjson>=0.3.1",

View file

@ -380,41 +380,34 @@ class AtcoderScraper(BaseScraper):
credentials: dict[str, str],
) -> SubmitResult:
def _submit_sync() -> SubmitResult:
try:
from camoufox.sync_api import Camoufox
except ImportError:
return SubmitResult(
success=False,
error="camoufox is required for AtCoder login. Install it: uv add 'camoufox[geoip]'",
)
from curl_cffi import requests as curl_requests
try:
with Camoufox(headless=True) as browser:
page = browser.new_page()
page.goto(f"{BASE_URL}/login", wait_until="domcontentloaded")
page.wait_for_load_state("networkidle")
page.fill('input[name="username"]', credentials.get("username", ""))
page.fill('input[name="password"]', credentials.get("password", ""))
page.click('#submit')
page.wait_for_url(lambda url: "/login" not in url, timeout=30000)
cookies = page.context.cookies()
session = curl_requests.Session(impersonate="chrome")
login_page = session.get(f"{BASE_URL}/login", timeout=TIMEOUT_SECONDS)
login_page.raise_for_status()
soup = BeautifulSoup(login_page.text, "html.parser")
csrf_input = soup.find("input", {"name": "csrf_token"})
if not csrf_input or not hasattr(csrf_input, "get"):
return SubmitResult(
success=False, error="Could not find CSRF token on login page"
for cookie in cookies:
session.cookies.set(
cookie["name"],
cookie["value"],
domain=cookie.get("domain", ""),
)
csrf_token = csrf_input.get("value", "") or "" # type: ignore[union-attr]
login_resp = session.post(
f"{BASE_URL}/login",
data={
"username": credentials.get("username", ""),
"password": credentials.get("password", ""),
"csrf_token": csrf_token,
},
timeout=TIMEOUT_SECONDS,
allow_redirects=False,
)
if login_resp.status_code in (301, 302):
location = login_resp.headers.get("Location", "")
if "/login" in location:
return SubmitResult(
success=False,
error="Login failed: incorrect username or password",
)
session.get(BASE_URL + location, timeout=TIMEOUT_SECONDS)
else:
login_resp.raise_for_status()
submit_page = session.get(
f"{BASE_URL}/contests/{contest_id}/submit",

11
scripts/ci.sh Executable file
View file

@ -0,0 +1,11 @@
#!/bin/sh
set -eu
nix develop --command stylua --check .
git ls-files '*.lua' | xargs nix develop --command selene --display-style quiet
nix develop --command prettier --check .
nix develop --command lua-language-server --check . --checklevel=Warning
nix develop --command uvx ruff format --check .
nix develop --command uvx ruff check .
nix develop --command uvx ty check .
nix develop --command uv run pytest tests/ -v

1168
uv.lock generated

File diff suppressed because it is too large Load diff