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
161 changes: 161 additions & 0 deletions .agents/INSTRUCTIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Repository instructions

## What this repository is

`pipx-install-action` is a GitHub Action (plain JavaScript/CommonJS, run on
Node.js) that installs Python command-line tools with
[pipx](https://github.com/pypa/pipx) inside a GitHub Actions workflow, with
GitHub Actions cache support so repeat runs skip reinstalling. It's published to
the GitHub Marketplace as `python-build-tools/pipx-install-action` and consumed
via `uses:` in other repos' workflows — there is no server/deploy step, just
tagged releases of this repository.

## Stack summary

| Aspect | Detail |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| Language | JavaScript, CommonJS (`require`/`module.exports`), no TypeScript |
| Package manager | npm (`package-lock.json` committed) |
| Build/package | [`@vercel/ncc`](https://github.com/vercel/ncc) bundles `src/index.js` into the single committed `dist/index.js` that GitHub Actions actually runs |
| Testing | Jest (`__tests__/*.test.js`), coverage badge generated to `badges/coverage.svg` |
| Infrastructure | None — runs entirely on GitHub-hosted runners via the `runs.using: node24` runtime in `action.yml` |

## Repository map

```text
.
├── action.yml # Action metadata: inputs, runs.using (node24), main: dist/index.js
├── src/
│ ├── index.js # Entrypoint, calls main.run()
│ ├── main.js # Reads inputs via @actions/core, calls pipxInstall
│ └── pipx-install.js # Core logic: reads pyproject.toml, caches/installs via pipx
├── dist/ # ncc-bundled output actually executed by GitHub Actions.
│ # GENERATED — do not hand-edit. Regenerate with `npm run package`.
├── __tests__/ # Jest tests + fixtures (__tests__/data/*.toml, sample workflow)
├── badges/coverage.svg # GENERATED by `npm test` — do not hand-edit
├── .github/workflows/
│ ├── ci.yml # Unit tests + lint/format + end-to-end action smoke test
│ ├── check-dist.yml # Rebuilds dist/ and fails if the committed copy differs
│ ├── linter.yml # super-linter over the whole repository (dist/ excluded)
│ └── jekyll-gh-pages.yml # Docs site deploy (unrelated to action correctness)
├── .github/codeql/codeql-config.yml # paths-ignore (node_modules, dist) for CodeQL
│ # default setup — see CI/CD section below
├── .github/dependabot.yml # Weekly npm + github-actions update groups
└── package.json
```

## Golden commands

```bash
npm ci # install exactly what's in package-lock.json
npm run ci-test # jest only, no coverage badge write (what CI runs)
npm test # jest + regenerate badges/coverage.svg
npm run lint # eslint .
npm run format:check # prettier --check .
npm run format:write # prettier --write .
npm run package # ncc build src/index.js -> dist/index.js (required before commit)
npm run all # format:write + lint + test + package — run this before every commit
npm run act-test # exercise __tests__/data workflow fixtures via `act`, if installed
```

This repository requires **Node.js >= 24** (`engines.node` in `package.json`,
matching `runs.using: node24` in `action.yml`). If multiple Node versions are
managed locally (e.g. via `fnm`), switch to 24 before running any of the above.

## CI/CD and required checks

- **`ci.yml`** (PR + push to `main`): runs `format:check`, `lint`, and `ci-test`
on Ubuntu, then exercises the action end-to-end (`uses: ./`) on both
`ubuntu-latest` and `windows-latest` against a fixture `pyproject.toml`.
- **`check-dist.yml`** (PR + push to `main`): runs `npm run bundle` (format +
package) and fails if `git diff dist/` is non-empty. **This is the check that
most commonly fails** — it fails on _any_ PR (Dependabot or human) that
changes `src/` or a dependency affecting the bundle without regenerating
`dist/`.
- **`linter.yml`** (PR + push to `main`): super-linter over the repository,
excluding `dist/`.
- **`jekyll-gh-pages.yml`** (push to `main`): deploys the docs site; unrelated
to whether the action itself works.
- **CodeQL** runs via GitHub's code scanning **default setup** (a repository
setting, not a checked-in workflow — there used to be a `codeql-analysis.yml`
"advanced setup" workflow here, but it was removed because it conflicted with
default setup: GitHub refuses advanced-config SARIF uploads for a language
default setup already owns, and it fails that way on every branch,
Dependabot's own PRs included). The `github-codeql-config-file` repository
property points default setup at `.github/codeql/codeql-config.yml` so `dist/`
and `node_modules` stay excluded from analysis.

## Constraints / hard rules

- **Always run `npm run all` before committing** any change to `src/`,
`package.json`, or `package-lock.json`. `dist/index.js` and
`badges/coverage.svg` are generated artifacts committed to the repository —
`check-dist.yml` enforces that `dist/` matches a fresh build.
- **Do not bump `@actions/core` past `^2.x`, `@actions/cache` past `^5.x`, or
`@actions/exec` past `^2.x`** without first migrating `src/` and `__tests__/`
off CommonJS. Those packages went **ESM-only** at `@actions/core@3.0.0`,
`@actions/cache@6.0.0`, and `@actions/exec@3.0.0` respectively (`require()` of
them throws at runtime). Dependabot and `npm audit fix --force` do not know
this and will cheerfully propose the breaking major — check each package's
`RELEASES.md` on GitHub before accepting a major-version bump for anything
under `@actions/*`.
- Don't add a dependency, or a `require()`/`import` of one, without actually
using it. This repository previously carried an unused `@actions/github`
import (dead since the original template scaffold) and an unused
`prettier-eslint` devDependency — both did nothing but accumulate Dependabot
security alerts for years. Prefer deleting dead imports over upgrading them.
- Check the GitHub Advisory Database (or `npm audit`) before adding or upgrading
a dependency, and don't just take the highest version `npm audit fix --force`
offers — verify it doesn't cross a breaking-change boundary like the one
above.
- No secrets, tokens, or credentials in code, tests, or workflow files.
- No protected files beyond `dist/` and `badges/coverage.svg` (generated, but
must still be committed alongside the source change that produced them).

## Common pitfalls

1. **Hand-editing `dist/index.js`.** It's generated by `npm run package`. Any
manual edit is silently discarded the next time someone runs the build, and
the diff will fail `check-dist.yml` review in the meantime.
1. **Forgetting to rebuild `dist/` before committing.** This is the single most
common reason a PR fails CI here — including Dependabot's own PRs, since
Dependabot never runs `npm run package` after bumping a dependency.
1. **Accepting a Dependabot major-version bump for an `@actions/*` toolkit
package without checking for an ESM-only breaking change.** See Constraints
above — this silently breaks the action at runtime (`ERR_REQUIRE_ESM`), which
the test suite may not catch if the module is mocked in tests rather than
exercised for real.
1. **Running against an older local Node version.** `engines.node` requires
`>=24`; older versions may pass tests locally but don't match the `node24`
runtime GitHub Actions uses to execute `dist/index.js`.
1. **Trusting `npm audit fix --force` output as-is.** It optimizes purely for
"no known vulnerabilities" and will happily select a major version that
breaks the module system for this repository. Cross-check against each
package's changelog first.
1. **Adding an `actions/setup-python` step to the `windows-latest` job in
`ci.yml`.** Don't — it's deliberately absent, mirroring `ubuntu-latest`,
which also has none. Both images ship `pipx` preinstalled (see
[actions/runner-images](https://github.com/actions/runner-images)), but only
against each image's own cached default Python patch version (`3.12.3` on
Ubuntu, `3.12.10` on Windows as of this writing — the two drift
independently). Pinning an exact patch via `setup-python` that doesn't match
what's cached makes it delete the cached interpreter and download a fresh one
with no `pipx` registered against it. An earlier version of this job did
exactly that (to reproduce a now-fixed cross-platform cache-key collision bug
— see `imageOs`/`imageVersion` in `pipx-install.js` and the
`ImageOS`/`ImageVersion` cases in `pipx-install.test.js`, which cover that
regression directly); it's no longer needed and was removed.

## Links

- [README.md](../README.md) — usage docs for consumers of the action
- [.github/workflows/ci.yml](../.github/workflows/ci.yml)
- [.github/workflows/check-dist.yml](../.github/workflows/check-dist.yml)
- [.github/workflows/linter.yml](../.github/workflows/linter.yml)
- [.github/codeql/codeql-config.yml](../.github/codeql/codeql-config.yml) —
consumed by CodeQL default setup via the `github-codeql-config-file`
repository property
- [.github/dependabot.yml](../.github/dependabot.yml)

Trust these instructions first; search the repository only when something is
missing or incorrect.
2 changes: 1 addition & 1 deletion .github/workflows/check-dist.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:

- name: Setup Node.js
id: setup-node
uses: actions/setup-node@v5
uses: actions/setup-node@v7
with:
node-version-file: package.json
cache: npm
Expand Down
6 changes: 1 addition & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:

- name: Setup Node.js
id: setup-node
uses: actions/setup-node@v5
uses: actions/setup-node@v7
with:
node-version-file: package.json
cache: npm
Expand Down Expand Up @@ -66,10 +66,6 @@ jobs:
id: checkout
uses: actions/checkout@v4

- uses: actions/setup-python@v6
with:
python-version: '3.12.3' # Use the exact same version as currently in effect for ubuntu-latest

- run: cp __tests__/data/pyproject.test1.toml pyproject.toml

- name: Test Local Action
Expand Down
74 changes: 0 additions & 74 deletions .github/workflows/codeql-analysis.yml

This file was deleted.

4 changes: 2 additions & 2 deletions .github/workflows/jekyll-gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v5
uses: actions/configure-pages@v6
- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
with:
Expand All @@ -48,4 +48,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v5
2 changes: 1 addition & 1 deletion .github/workflows/linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:

- name: Setup Node.js
id: setup-node
uses: actions/setup-node@v5
uses: actions/setup-node@v7
with:
node-version-file: package.json
cache: npm
Expand Down
25 changes: 25 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!-- For repository-specific context (what this repository is, stack, commands,
CI/CD), see .agents/INSTRUCTIONS.md. This file holds only hard rules. -->

# Agent instructions

For full repository-specific context (stack, golden commands, CI/CD, pitfalls),
see [.agents/INSTRUCTIONS.md](.agents/INSTRUCTIONS.md).

## Constraints

- Never commit a change to `src/`, `package.json`, or `package-lock.json`
without running `npm run all` first (or at minimum `npm run package`).
`dist/index.js` is the file GitHub Actions actually executes, and
`check-dist.yml` CI fails if the committed `dist/` doesn't match a fresh
build.
- Do not bump `@actions/core`, `@actions/cache`, or `@actions/exec` past their
last CommonJS-compatible major (`^2.x`, `^5.x`, `^2.x` respectively) — later
majors are ESM-only and this repository's `src/` uses `require()`. See
INSTRUCTIONS.md before accepting any Dependabot PR proposing these bumps.
- Don't add or `require()` a dependency without using it. Check the GitHub
Advisory Database / `npm audit` before adding or upgrading dependencies.
- No secrets, tokens, or credentials in code, tests, or workflow files.

Trust these instructions first; search the repository only when something is
missing or incorrect.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# [pipx-install GitHub Action](https://github.com/brandonlwhite/pipx-install-action)
# [pipx-install GitHub Action](https://github.com/python-build-tools/pipx-install-action)

[![GitHub Super-Linter](https://github.com/brandonlwhite/pipx-install-action/actions/workflows/linter.yml/badge.svg)](https://github.com/super-linter/super-linter)
![CI](https://github.com/brandonlwhite/pipx-install-action/actions/workflows/ci.yml/badge.svg)
![Test Converage](https://raw.githubusercontent.com/BrandonLWhite/pipx-install-action/main/badges/coverage.svg)
[![GitHub Super-Linter](https://github.com/python-build-tools/pipx-install-action/actions/workflows/linter.yml/badge.svg)](https://github.com/super-linter/super-linter)
![CI](https://github.com/python-build-tools/pipx-install-action/actions/workflows/ci.yml/badge.svg)
![Test Converage](https://raw.githubusercontent.com/python-build-tools/pipx-install-action/main/badges/coverage.svg)

This action installs Python tools using [pipx](https://github.com/pypa/pipx).

Expand Down Expand Up @@ -32,7 +32,7 @@ Here's an example of how to use this action in a workflow file:
```yaml
steps:
- name: Install Python Tools
uses: BrandonLWhite/pipx-install-action
uses: python-build-tools/pipx-install-action
```

Tool packages are expressed in your project's `pyproject.toml` file
Expand Down
11 changes: 0 additions & 11 deletions __tests__/main.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
const path = require('path')
const fs = require('fs')
const core = require('@actions/core')
const github = require('@actions/github')
const yaml = require('js-yaml')
const main = require('../src/main')

Expand Down Expand Up @@ -45,11 +44,6 @@ describe('action', () => {
})

it('logs if nothing to do', async () => {
// Mock the action's payload
github.context.payload = {
actor: 'mona'
}

await main.run()

expect(runMock).toHaveReturned()
Expand All @@ -59,11 +53,6 @@ describe('action', () => {
it('sets a failed status', async () => {
inputs['install-config-file'] = 'failfail.fail'

// Mock the action's payload
github.context.payload = {
actor: 'mona'
}

await main.run()

expect(runMock).toHaveReturned()
Expand Down
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ inputs:
default: true

runs:
using: node20
using: node24
main: dist/index.js
2 changes: 1 addition & 1 deletion badges/coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading