Repository navigation
Add a shared pup action #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,93 @@ | ||||||
| # pup | ||||||
|
|
||||||
| Installs [pup](https://github.com/stellarwp/pup) from its GitHub release and, when asked, reads the | ||||||
| version and the zip name from it. Every action in this repository that needs pup uses this one, so | ||||||
| pup is downloaded, checked and read the same way everywhere, and no action depends on where a | ||||||
| repository keeps its own copy. | ||||||
|
|
||||||
| pup is a PHP archive. It is downloaded into the runner's temp directory and run with the runner's | ||||||
| PHP, so it never ends up in a commit or a zip. | ||||||
|
|
||||||
| ```yaml | ||||||
| - uses: actions/checkout@v7 | ||||||
|
|
||||||
| - name: Install pup and read the zip name | ||||||
| id: pup | ||||||
| uses: stellarwp/plugin-toolbox/.github/actions/pup@main | ||||||
| with: | ||||||
| pup-version: '2.0.0' | ||||||
| read: zip-name | ||||||
| dev: 'true' | ||||||
|
|
||||||
| - run: echo "${{ steps.pup.outputs.zip-name }}.zip" | ||||||
| ``` | ||||||
|
|
||||||
| Inside this repository, call it as `uses: $/.github/actions/pup`. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix the in-repository
📝 Proposed fix
Committable suggestion
|
||||||
|
|
||||||
| ## Inputs | ||||||
|
|
||||||
| | Input | Default | What it does | | ||||||
| |---|---|---| | ||||||
| | `pup-version` | `2.0.0` | The pup release: a number such as `2.0.0`, or `latest`. See [Which pup](#which-pup) | | ||||||
| | `read` | empty | Empty installs only. `version` also runs `pup get-version`. `zip-name` also runs `pup zip-name` for that version. Both need a checkout | | ||||||
| | `dev` | `false` | `true` reads the development build version, `pup get-version --dev`, which has the commit in it. Only used when `read` is set | | ||||||
| | `token` | `github.token` | Authenticates the `latest` lookup. A pinned release makes no API call | | ||||||
|
|
||||||
| ## Outputs | ||||||
|
|
||||||
| | Output | What it is | | ||||||
| |---|---| | ||||||
| | `phar` | Path of `pup.phar`. Run any pup command with `php "$PHAR" <command>` | | ||||||
| | `release` | The release that was installed. `latest` becomes its number | | ||||||
| | `version` | What `pup get-version` printed. Empty unless `read` is set | | ||||||
| | `zip-name` | The zip's name, without `.zip`. Empty unless `read` is `zip-name` | | ||||||
|
|
||||||
| Both values are the last line pup printed, so a PHP notice printed before it is never part of them. | ||||||
| PHP's errors go to stderr (`display_errors=stderr`) for the same reason. | ||||||
|
|
||||||
| ## Which pup | ||||||
|
|
||||||
| `pup-version` pins a release or tracks the newest one with `latest`. A pinned release downloads | ||||||
| straight from its release page. `latest` asks the GitHub API for the newest release. | ||||||
|
|
||||||
| The release must ship a `pup.phar` asset, which every release from 1.2.4 to 2.0.0 does. The action | ||||||
| checks that with a HEAD request. A release without a phar fails with a message naming a pin to use | ||||||
| instead, and a number that is not a release at all (a typo) fails with "is not a release". | ||||||
|
|
||||||
| Use the release the repository's own `composer pup` script downloads. The zip name pup makes can | ||||||
| depend on its release, and the name read here has to equal the name of the zip the repository's own | ||||||
| build made. | ||||||
|
|
||||||
| Within one job the phar is downloaded once. A second use of the action, or of the script below, | ||||||
| finds the file and reuses it. | ||||||
|
|
||||||
| ## From another action's script | ||||||
|
|
||||||
| `pup.js` is a plain Node module. Its functions take the `exec` toolkit, so a script that | ||||||
| github-script runs can pass the one it was given: | ||||||
|
|
||||||
| ```js | ||||||
| const { installPup, getVersion, zipName, runPup } = require('../pup/pup.js') | ||||||
|
|
||||||
| const { phar } = await installPup({ core, exec, version: '2.0.0', token: '', env: process.env }) | ||||||
| const version = await getVersion({ exec, phar, cwd, dev: true }) | ||||||
| const name = await zipName({ exec, phar, cwd, version }) | ||||||
| const output = await runPup({ exec, phar, cwd, args: ['check'] }) | ||||||
| ``` | ||||||
|
|
||||||
| `runPup` runs any pup command and returns what it printed, or throws with the output when pup exits | ||||||
| non-zero. The module also exports `downloadPhar`, `pharPath`, `validatePupVersion`, | ||||||
| `resolvePupRelease`, `resolvePupDistribution`, `pupDownloadUrl` and `pupReleaseUrl`. Release prep | ||||||
| uses them for its own steps. | ||||||
|
|
||||||
| The path is relative to the file that requires it. github-script's own `require` resolves against | ||||||
| the workspace of the repository being built, which is why an action's manifest passes | ||||||
| `${{ github.action_path }}`. | ||||||
|
|
||||||
| ## How it runs | ||||||
|
|
||||||
| The logic is in `pup.js`, run by actions/github-script, which supplies the Node runtime and `exec`. | ||||||
| There is no dependency to install and no bundle to build: the file in the repository is the file | ||||||
| that runs. Inputs reach the script through `process.env`, never by being interpolated into it. | ||||||
| `tests/actions/pup/pup.test.js` runs it against a stand-in for `exec` and for `fetch`. Run | ||||||
| `npm install` once, then `node --test`. | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| # Installs pup, and optionally reads the version and the zip name from it. | ||
| # | ||
| # pup is downloaded from its GitHub release into the runner's temp directory and run with PHP, so a | ||
| # repository does not need a copy of it, and every action in this repository installs and reads pup | ||
| # the same way. The work is in pup.js, run by actions/github-script, so there is nothing to install | ||
| # and nothing to build. | ||
| name: pup | ||
| description: Installs pup from its GitHub release and optionally reads the version and the zip name | ||
|
|
||
| inputs: | ||
| pup-version: | ||
| description: >- | ||
| The pup release to install: a release number, such as `2.0.0`, or `latest` for the newest | ||
| release. It has to ship a pup.phar asset, which every release from 1.2.4 to 2.0.0 does. Use the | ||
| release the repository's own `composer pup` script downloads: a zip name made by another | ||
| release could differ from the one the repository's build makes. | ||
| required: false | ||
| default: '2.0.0' | ||
| read: | ||
| description: >- | ||
| What to read from pup after installing it. Empty reads nothing. `version` runs | ||
| `pup get-version`. `zip-name` reads the version, then runs `pup zip-name` for it. Both need a | ||
| checkout of the repository, because pup reads the version from its files. | ||
| required: false | ||
| default: '' | ||
| dev: | ||
| description: >- | ||
| `true` reads the development build version, `pup get-version --dev`, which has the commit in | ||
| it. Only used when `read` is set. | ||
| required: false | ||
| default: 'false' | ||
| token: | ||
| description: >- | ||
| Token for the `latest` lookup through the GitHub API, so the runner's shared unauthenticated | ||
| quota is not spent. A pinned release makes no API call and needs no token. | ||
| required: false | ||
| default: ${{ github.token }} | ||
|
|
||
| outputs: | ||
| phar: | ||
| description: 'Path of pup.phar. Run it with `php "$PHAR" <command>`.' | ||
| value: ${{ steps.pup.outputs.phar }} | ||
| release: | ||
| description: 'The pup release that was installed, as a tag. `latest` is resolved to its number.' | ||
| value: ${{ steps.pup.outputs.release }} | ||
| version: | ||
| description: 'The version pup printed. Empty unless `read` is `version` or `zip-name`.' | ||
| value: ${{ steps.pup.outputs.version }} | ||
| zip-name: | ||
| description: 'The name of the zip, without `.zip`. Empty unless `read` is `zip-name`.' | ||
| value: ${{ steps.pup.outputs.zip-name }} | ||
|
|
||
| runs: | ||
| using: 'composite' | ||
| steps: | ||
| - name: Install pup | ||
| id: pup | ||
| uses: actions/github-script@v9 | ||
| env: | ||
| # github-script's require() resolves against the workspace of the repository being built, | ||
| # not this action's directory, so the path has to be passed in. | ||
| ACTION_PATH: ${{ github.action_path }} | ||
| # Read from process.env rather than interpolated into the script, so an input is data and | ||
| # never part of the program. | ||
| INPUT_PUP_VERSION: ${{ inputs.pup-version }} | ||
| INPUT_READ: ${{ inputs.read }} | ||
| INPUT_DEV: ${{ inputs.dev }} | ||
| INPUT_TOKEN: ${{ inputs.token }} | ||
| with: | ||
| script: | | ||
| const { run } = require(`${process.env.ACTION_PATH}/pup.js`) | ||
| await run({ core, exec, env: process.env }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script executed:
Repository: stellarwp/plugin-toolbox
Length of output: 8806
Do not pin the pup example to
@main.Consumers in this repository use major-version tags. The other action READMEs and the release-prep template follow that practice.
This action is new in this PR. A
@v1tag that exists now may not contain.github/actions/pup. Update the example to the major-version tag that includes this action after the release that publishes it.