Skip to content

Commit 8900987

Browse files
authored
Merge pull request #21 from ArchiveBox/claude/add-shebang-dependencies-0Tijt
Add --script mode to run command for inline metadata scripts
2 parents 160ec2f + 18eeb66 commit 8900987

39 files changed

Lines changed: 1614 additions & 794 deletions

.github/workflows/deploy-pages.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ permissions:
1111
id-token: write
1212

1313
concurrency:
14-
group: "pages"
15-
cancel-in-progress: false
14+
group: ${{ github.workflow }}-${{ github.ref }}
15+
cancel-in-progress: true
1616

1717
jobs:
1818
build:
1919
runs-on: ubuntu-latest
20+
timeout-minutes: 20
2021
steps:
2122
- name: Checkout
2223
uses: actions/checkout@v6
@@ -49,6 +50,7 @@ jobs:
4950
url: ${{ steps.deployment.outputs.page_url }}
5051
runs-on: ubuntu-latest
5152
needs: build
53+
timeout-minutes: 20
5254
steps:
5355
- name: Deploy to GitHub Pages
5456
id: deployment

.github/workflows/release.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,14 @@ permissions:
1313
env:
1414
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
1515

16+
concurrency:
17+
group: ${{ github.workflow }}-${{ github.ref }}
18+
cancel-in-progress: true
19+
1620
jobs:
1721
release-state:
1822
runs-on: ubuntu-latest
23+
timeout-minutes: 20
1924
steps:
2025
- uses: actions/checkout@v6
2126
with:

.github/workflows/tests.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,14 @@ permissions:
1313
env:
1414
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
1515

16+
concurrency:
17+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
18+
cancel-in-progress: true
19+
1620
jobs:
1721
precheck:
1822
runs-on: ubuntu-latest
23+
timeout-minutes: 20
1924

2025
steps:
2126
- uses: actions/checkout@v6
@@ -45,6 +50,7 @@ jobs:
4550
discover-standard-tests:
4651
needs: precheck
4752
runs-on: ubuntu-latest
53+
timeout-minutes: 20
4854
outputs:
4955
test-files: ${{ steps.set-matrix.outputs.test-files }}
5056

@@ -75,6 +81,7 @@ jobs:
7581
name: ${{ matrix.target.os }} py${{ matrix.target.python_version }} ${{ matrix.test.name }}
7682
needs: [precheck, discover-standard-tests]
7783
runs-on: ${{ matrix.target.os }}
84+
timeout-minutes: 20
7885
if: ${{ needs.discover-standard-tests.outputs.test-files != '[]' }}
7986
strategy:
8087
fail-fast: false
@@ -204,6 +211,7 @@ jobs:
204211
discover-live-tests:
205212
needs: precheck
206213
runs-on: ubuntu-latest
214+
timeout-minutes: 20
207215
outputs:
208216
live-tests: ${{ steps.set-matrix.outputs.live-tests }}
209217

@@ -245,6 +253,7 @@ jobs:
245253
name: ${{ matrix.live.name }}
246254
needs: [precheck, discover-live-tests]
247255
runs-on: ${{ matrix.live.os }}
256+
timeout-minutes: 20
248257
if: ${{ needs.discover-live-tests.outputs.live-tests != '[]' }}
249258
strategy:
250259
fail-fast: false

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ venv/
1414
.env
1515
*.log
1616
.DS_Store
17+
uv.lock

README.md

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,36 @@ abx --binproviders=env,uv,pip,apt,brew yt-dlp # restrict provider resolution
123123

124124
Options before the binary name (`--lib`, `--binproviders`, `--dry-run`, `--debug`, `--no-cache`, `--update`) are forwarded to `abx-pkg`; everything after the binary name is forwarded to the binary itself.
125125

126+
#### Shebang Line in Scripts
127+
128+
Inspired by [`uv`'s inline script metadata](https://docs.astral.sh/uv/guides/scripts/#declaring-script-dependencies), `abx-pkg` lets you declare **arbitrary package dependencies** at the top of any script using a `/// script` metadata block.
129+
130+
```javascript
131+
#!/usr/bin/env -S abx-pkg run --script node
132+
133+
// /// script
134+
// dependencies = [
135+
// {name = "node", binproviders = ["env", "apt", "brew"], min_version = "22.0.0"},
136+
// {name = "playwright", binproviders = ["pnpm", "npm"]},
137+
// {name = "chromium", binproviders = ["playwright", "puppeteer", "apt"], min_version = "131.0.0"},
138+
// ]
139+
// [tool.abx-pkg]
140+
// ABX_PKG_POSTINSTALL_SCRIPTS = true
141+
// ///
142+
143+
const { chromium } = require('playwright');
144+
145+
(async () => {
146+
const browser = await chromium.launch();
147+
const page = await browser.newPage();
148+
await page.goto('https://example.com');
149+
console.log(await page.title());
150+
await browser.close();
151+
})();
152+
```
153+
154+
The metadata parser is comment-syntax-agnostic — it looks for `/// script` and `///` delimiters and strips the first whitespace-delimited token from each line, so `#`, `//`, `--`, `;`, and any other single-token comment prefix all work.
155+
126156
#### Per-`Binary` / per-`BinProvider` options as CLI flags
127157

128158
Every [`Binary` / `BinProvider` configuration field](#configuration) is exposed as a CLI flag on the group and on subcommands (`install`, `update`, `uninstall`, `load`), and is also available to `run` / `abx` via group-level flags placed before the binary name. Providers that can't enforce a given option emit a warning to `stderr` and continue — no hard failure.
@@ -373,8 +403,9 @@ class CargoProvider(BinProvider):
373403
timeout: int | None = None,
374404
) -> str:
375405
install_args = install_args or self.get_install_args(bin_name)
376-
installer = self._require_installer_bin()
377-
proc = self.exec(bin_name=installer, cmd=['install', *install_args], timeout=timeout)
406+
installer = self.INSTALLER_BINARY()
407+
assert installer and installer.loaded_abspath
408+
proc = self.exec(bin_name=installer.loaded_abspath, cmd=['install', *install_args], timeout=timeout)
378409
if proc.returncode != 0:
379410
self._raise_proc_error('install', install_args, proc)
380411
return proc.stdout.strip() or proc.stderr.strip()

abx_pkg/base_types.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,6 @@
2121
# provider; explicit constructor kwargs override both.
2222
DEFAULT_LIB_DIR: Path = user_config_path("abx") / "lib"
2323

24-
_lib_dir_env = os.environ.get("ABX_PKG_LIB_DIR", "").strip()
25-
ABX_PKG_LIB_DIR: Path | None = (
26-
Path(_lib_dir_env).expanduser().resolve() if _lib_dir_env else None
27-
)
28-
2924

3025
def abx_pkg_install_root_default(provider_name: str) -> Path | None:
3126
"""Resolve a provider's default install root from env vars.
@@ -42,8 +37,11 @@ def abx_pkg_install_root_default(provider_name: str) -> Path | None:
4237
specific = os.environ.get(f"ABX_PKG_{provider_name.upper()}_ROOT", "").strip()
4338
if specific:
4439
return Path(specific).expanduser().resolve()
45-
if ABX_PKG_LIB_DIR is not None:
46-
return ABX_PKG_LIB_DIR / provider_name
40+
# Read from os.environ directly (not the cached module-level
41+
# ABX_PKG_LIB_DIR) because the CLI sets it at runtime via --lib.
42+
lib_dir = os.environ.get("ABX_PKG_LIB_DIR", "").strip()
43+
if lib_dir:
44+
return Path(lib_dir).expanduser().resolve() / provider_name
4745
return None
4846

4947

0 commit comments

Comments
 (0)