ci: scripts + format
This commit is contained in:
parent
f2e312f860
commit
8c8e49d75c
9 changed files with 1279 additions and 35 deletions
|
|
@ -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
1
.styluaignore
Normal file
|
|
@ -0,0 +1 @@
|
|||
.direnv/
|
||||
42
flake.nix
42
flake.nix
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
11
scripts/ci.sh
Executable 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue