diff --git a/.github/workflows/archify.yml b/.github/workflows/archify.yml index 9829416..930fc7d 100644 --- a/.github/workflows/archify.yml +++ b/.github/workflows/archify.yml @@ -1,12 +1,12 @@ name: archify -# Opens a PR when tt-a1i/archify cuts a release newer than vendor/archify.lock.json. docs/archify.md. +# Pushes branch archify/ when tt-a1i/archify cuts a release newer than vendor/archify.lock.json, then stops: GitHub Actions +# may not open pull requests in this repository, so the run leaves a notice and a person opens the PR. docs/archify.md. on: schedule: - cron: '0 14 * * 1' workflow_dispatch: permissions: contents: write - pull-requests: write jobs: bump: runs-on: ubuntu-latest @@ -21,11 +21,11 @@ jobs: else echo "tag=$(curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/repos/tt-a1i/archify/releases/latest | jq -r .tag_name)" >> "$GITHUB_OUTPUT"; fi - if: steps.check.outputs.tag != '' env: - GH_TOKEN: '${{ github.token }}' GITHUB_TOKEN: '${{ github.token }}' TAG: '${{ steps.check.outputs.tag }}' run: | - git ls-remote --exit-code origin "refs/heads/archify/$TAG" && { echo "archify/$TAG already open"; exit 0; } + PR="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/compare/main...archify/$TAG?expand=1" + git ls-remote --exit-code origin "refs/heads/archify/$TAG" && { echo "::notice title=archify $TAG::Branch archify/$TAG is pushed and not merged yet. Open the PR by hand: $PR"; exit 0; } node bin/archify-sync.mjs --tag "$TAG" node --test test/archify.test.mjs git config user.name 'github-actions[bot]' @@ -34,4 +34,4 @@ jobs: git add vendor CHANGELOG.md docs/archify.md git commit -m "archify: vendor $TAG" git push origin "archify/$TAG" - gh pr create --title "archify: vendor $TAG" --body "Moves vendor/archify to $TAG with bin/archify-sync.mjs. Release notes: https://github.com/tt-a1i/archify/releases/tag/$TAG. The archify gate passed in this run; close and reopen the PR to start the full test workflow." + echo "::notice title=archify $TAG::Pushed branch archify/$TAG; the archify gate passed in this run. Open the PR by hand: $PR" diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ef549a..6012771 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog ## Unreleased +- **The archify workflow stops at the pushed branch.** The scheduled run on 2026-09-28 vendored archify v3.0.0, pushed `archify/v3.0.0` and then failed at `gh pr create`: GitHub Actions may not open pull requests in this repository. The workflow now pushes the branch, leaves a notice that links the compare page and exits 0. It repeats the notice each week until the branch is merged, and it no longer asks for the `pull-requests` permission. `docs/archify.md` says to open the PR by hand. - **Chart bars are square at both ends (#162).** The engine painted every `bar` row with `border-radius:6px 6px 0 0`, so a column was rounded on its value end and an `hbar` bar on its top corners, and each read as a tab. `template.html` now paints no radius on a bar: every chart form (`bar`, `hbar`, a `compare` series, the template bars) is square by default, and a row that sets `radius` rounds all four corners. SKILL.md's row table and `docs/charts.md` say so. Deck and library html rebuilt. Gates: test/chart.test.mjs asserts that the chart rows and every template's rows carry no one-sided radius, and renders the chart forms and the chart templates in Chromium to check that each bar is square and that no mark draws a one-sided radius. - **Undo and Delete buttons in the HUD.** Undo (⌘Z) and delete (⌫) were keys only, so a phone could neither take back an edit nor remove a row or a slide (#167). The edit group now carries an Undo button (Lucide `undo-2`) and a Delete button (Lucide `trash-2`) on every pointer, after duplicate. Each runs the code its key runs in the same place: the browser's text undo and delete while a row is being edited, `sheetKey()` in the contact sheet, `undo()` and the new `delSel()` (the canvas key's own body, moved into a function both share) on the canvas. Undo is disabled while the undo stack is empty; Delete is disabled with no selection on the canvas, and in the sheet with no selection or one slide left. Neither appears in the present-mode pill. The HUD manifest in `docs/editor.md` gains `undobtn` and `delbtn`. Guarded by `test/hud.test.mjs` and a tap-only test in `test/touch.test.mjs`. - **A served deck spellchecks the words you type.** Issue 86: spellcheck ran at build time only, so a word typed after `create` never reached the badge, the wash, the thumbnail counts or the panel, and the underline seen while typing was the browser's own, gone once the caret left the row. `bin/serve.mjs` now answers `POST /__decklet/spell` with the refused words of one row and their suggestions, through `lib/spell.mjs` with the build's dictionary and the deck's `spell.ignore`; it takes the token and Host guards of the write endpoints and at most 500 words of 64 characters, and answers 503 when the dictionary is not installed. The editor probes the endpoint, sends a row's words when its edit commits (debounced), and merges the answer into the set it already paints. Each PUT rewrites the file's `SPELL` block from the saved model (`splice()` takes it as a fourth argument), so a reload shows the live flags. A deck from disk, from another host, or from a server without a dictionary keeps build-time checking, and the spellcheck button's tip says so. Tests: `test/serve.test.mjs` (endpoint, guards, the SPELL block on PUT, no dictionary), `test/serve-live.test.mjs` (type, commit, badge, wash, panel with suggestions, thumbnail, reload, fix, in every browser project), `test/spell.test.mjs` (a `file://` deck makes no request and shows the tip). diff --git a/docs/archify.md b/docs/archify.md index 5f1f140..2e4d6d8 100644 --- a/docs/archify.md +++ b/docs/archify.md @@ -38,4 +38,4 @@ node bin/archify-sync.mjs --check # exit 1 when a newer release is out The script replaces the tree, rewrites the lock, adds a line under `## Unreleased` in CHANGELOG.md and updates the tag in this page. Review the upstream release notes, run `npm test`, and ship it as its own PR. -The `archify` workflow runs `--check` every Monday. On a newer upstream release it runs the sync, runs the archify gate and opens a PR on branch `archify/`. A PR opened by the workflow's token starts no other workflow, so close and reopen it to run the full `test` gate before merging. +The `archify` workflow runs `--check` every Monday. On a newer upstream release it runs the sync, runs the archify gate, pushes branch `archify/` and stops with a notice on the run that links the compare page. GitHub Actions may not open pull requests in this repository, so open the PR by hand from that branch; the `test` gate starts on its own. Each later run repeats the notice until the branch is merged. diff --git a/test/archify.test.mjs b/test/archify.test.mjs index b977579..a5887fe 100644 --- a/test/archify.test.mjs +++ b/test/archify.test.mjs @@ -60,3 +60,14 @@ test('the pin is documented where a reader looks', () => { assert.match(read('docs/figures.md'), /\(archify\.md\)/, 'docs/figures.md must link docs/archify.md'); assert.ok(JSON.parse(read('package.json')).files.includes('vendor'), 'package.json files must ship vendor/'); }); + +// GitHub Actions may not open pull requests in this repository, so the workflow hands over a pushed branch and a notice. +test('the archify workflow stops at the pushed branch and asks for a PR by hand', () => { + const yml = read('.github/workflows/archify.yml'); + assert.doesNotMatch(yml, /gh pr create/, 'the workflow token cannot open a PR; the run must not try'); + assert.doesNotMatch(yml, /pull-requests:/, 'the workflow needs no pull-requests permission'); + assert.match(yml, /git push origin "archify\/\$TAG"/); + assert.equal((yml.match(/::notice title=archify \$TAG::/g) || []).length, 2, 'a notice on the run that pushes the branch and on each later run that finds it'); + assert.match(yml, /compare\/main\.\.\.archify\/\$TAG\?expand=1/, 'the notice links the page that opens the PR'); + assert.match(read('docs/archify.md'), /open the PR by hand/); +});