Docs: job summaries, re-runs and attempts, debug logging, cancelling, logs, badges
The Actions guide documents each, with the API routes; PLAN.md lists the run parity built and what is not yet.
2 files+168−60/2 viewed
| 40 | 40 | | Composite actions | The same. | | |
| 41 | 41 | | Reusable workflows in the repository (`jobs.<id>.uses: ./.g1t/workflows/build.yml`) | The same: `with:` inputs, `on.workflow_call` outputs, and nesting up to four deep. `./.github/workflows/…` finds the workflow under `.g1t/` after the move. Their jobs read the repository's secrets and variables. | | |
| 42 | 42 | | `actions/checkout` | Checks out from g1t, with `ref`, `fetch-depth`, `path`, `repository`, `token` and `submodules`. | | |
| 43 | − | | `GITHUB_OUTPUT`, `GITHUB_ENV`, `GITHUB_PATH`, `GITHUB_STATE`, `GITHUB_STEP_SUMMARY` | The same. | | |
| 43 | + | | `GITHUB_OUTPUT`, `GITHUB_ENV`, `GITHUB_PATH`, `GITHUB_STATE`, `GITHUB_STEP_SUMMARY` | The same. Step summaries show on the run's page; see [job summaries](#job-summaries). | | |
| 44 | 44 | | `::error::`, `::warning::`, `::notice::`, `::group::`, `::add-mask::` | The same: errors and warnings become annotations on the run, and [masked](#masking-secrets) values stay hidden. | | |
| 45 | 45 | | `secrets.*`, `vars.*`, `secrets.GITHUB_TOKEN` | The same. `secrets.G1T_TOKEN` is [the job's own token](#the-jobs-token); `GITHUB_TOKEN` is its alias. | | |
| 46 | 46 | | `environment:` on a job | The job waits for the environment's [protection rules](#environments), then reads each key's row for that environment, as environment secrets work, and the run records a [deployment](/guides/deployments-api/#deployments-from-g1t-actions) to it. `url` gives the deployment its address; `deployment: false` reads the environment's values without making one. The name may be an expression. | | |
| ⋯ | |||
| 621 | 621 | ||
| 622 | 622 | A run's page shows its jobs, each job's steps, and their logs as they are | |
| 623 | 623 | written. Groups fold, errors and warnings are marked, and secrets are | |
| 624 | − | replaced with `***`. **Cancel**, **Re-run all jobs** and **Re-run failed | |
| 625 | − | jobs** do what they say. | |
| 624 | + | replaced with `***`. | |
| 626 | 625 | ||
| 627 | 626 | The start of each job's log lists what its [token](#the-jobs-token) may do. | |
| 628 | 627 | ||
| 628 | + | ### Job summaries | |
| 629 | + | ||
| 630 | + | Markdown a step appends to the file in `$GITHUB_STEP_SUMMARY` shows at | |
| 631 | + | the top of the run's page, a card per job, in the order its steps wrote | |
| 632 | + | it: | |
| 633 | + | ||
| 634 | + | ```yaml | |
| 635 | + | - name: Report the tests | |
| 636 | + | if: always() | |
| 637 | + | run: | | |
| 638 | + | echo "### Test results" >> "$GITHUB_STEP_SUMMARY" | |
| 639 | + | echo "| Suite | Passed | Failed |" >> "$GITHUB_STEP_SUMMARY" | |
| 640 | + | echo "| --- | ---: | ---: |" >> "$GITHUB_STEP_SUMMARY" | |
| 641 | + | echo "| unit | 41 | 0 |" >> "$GITHUB_STEP_SUMMARY" | |
| 642 | + | ``` | |
| 643 | + | ||
| 644 | + | | What | How it works | | |
| 645 | + | | --- | --- | | |
| 646 | + | | Formatting | GitHub-flavoured Markdown: tables, task lists, alerts such as `> [!WARNING]`, code blocks, and the HTML GitHub allows. Scripts, styles and event handlers are removed. | | |
| 647 | + | | Secrets | Masked like the log, before the summary leaves the runner. | | |
| 648 | + | | Size | Up to 1 MiB a step. A larger summary is refused with an error in the step's log, as on GitHub. | | |
| 649 | + | | Steps | Up to 20 steps of a job keep a summary; later ones are dropped. | | |
| 650 | + | | Actions | A JavaScript action's `core.summary` writes to the same file, so it works unchanged. | | |
| 651 | + | ||
| 652 | + | Summaries belong to their attempt: an earlier attempt keeps its own. | |
| 653 | + | ||
| 654 | + | ### Re-running | |
| 655 | + | ||
| 656 | + | When a run has finished, someone with the Write role can run it again: | |
| 657 | + | ||
| 658 | + | | Button | Runs again | | |
| 659 | + | | --- | --- | | |
| 660 | + | | **Re-run all jobs** | Every job. | | |
| 661 | + | | **Re-run failed jobs** | Jobs that did not succeed (failed, cancelled or skipped), and every job that needs one of them. | | |
| 662 | + | | The re-run button beside a job's name | That job, and every job that needs it. A job of a matrix runs again with the rest of its matrix; a job of a reusable workflow runs again with the job that calls it. | | |
| 663 | + | ||
| 664 | + | Jobs that are not run again keep how they ended, and their outputs reach | |
| 665 | + | the jobs that need them. | |
| 666 | + | ||
| 667 | + | Each re-run is a new **attempt**. The run keeps its number, `github.run_attempt` | |
| 668 | + | goes up by one, and the attempt before is kept as it ended: its jobs, | |
| 669 | + | their steps, logs and summaries. Pick one from **Attempt #** at the top of | |
| 670 | + | the page to read it. Its jobs have ids of their own, so a link to an | |
| 671 | + | earlier attempt's job keeps showing that job's log. | |
| 672 | + | ||
| 673 | + | #### Debug logging | |
| 674 | + | ||
| 675 | + | Each re-run asks whether to **Enable debug logging**. The new attempt's | |
| 676 | + | jobs then run with: | |
| 677 | + | ||
| 678 | + | | Set | Effect | | |
| 679 | + | | --- | --- | | |
| 680 | + | | `RUNNER_DEBUG=1`, and `runner.debug` is `1` | Actions that check it, such as the toolkit's `core.isDebug()`, log more. | | |
| 681 | + | | `ACTIONS_STEP_DEBUG=true` | `::debug::` lines are shown in the log. | | |
| 682 | + | | `ACTIONS_RUNNER_DEBUG=true` | Set for actions that read it. | | |
| 683 | + | ||
| 684 | + | With debug logging, each step's log also says how its `if:` read: | |
| 685 | + | `Evaluating condition for step`, the expression, and the result. Setting a | |
| 686 | + | secret or variable named `ACTIONS_STEP_DEBUG` to `true` shows `::debug::` | |
| 687 | + | lines on every run instead. The attempt picker marks attempts that ran | |
| 688 | + | with debug logging. | |
| 689 | + | ||
| 690 | + | ### Cancelling | |
| 691 | + | ||
| 692 | + | **Cancel run** cancels jobs that have not started at once. A job that is | |
| 693 | + | running is stopped the way GitHub stops one: | |
| 694 | + | ||
| 695 | + | 1. The step it is on gets `SIGINT`, then `SIGTERM` 7.5 seconds later, and | |
| 696 | + | is killed 2.5 seconds after that. Signals reach the processes the step | |
| 697 | + | started too; in a [job container](#job-containers) they reach only the | |
| 698 | + | `docker exec` that runs the step. The step ends **cancelled**. | |
| 699 | + | 2. Its remaining steps run only if they ask to: `if: always()` or | |
| 700 | + | `if: cancelled()`. Steps without an `if:`, or with `success()` or | |
| 701 | + | `failure()`, are skipped. | |
| 702 | + | 3. Post steps (an action's `post`, saving the cache) run, as their | |
| 703 | + | `post-if` is `always()` unless the action says otherwise. | |
| 704 | + | 4. The job ends **cancelled**, whatever those steps came to. | |
| 705 | + | ||
| 706 | + | While that happens the run says **Cancelling**. A job still going 5 | |
| 707 | + | minutes after it was cancelled is stopped outright. **Force cancel** | |
| 708 | + | (shown while a run is cancelling) stops every job at once, without | |
| 709 | + | waiting for its cleanup steps. | |
| 710 | + | ||
| 711 | + | A step learns of a cancellation within about 10 seconds, even when it | |
| 712 | + | prints nothing. A job on a [self-hosted runner](/guides/self-hosted-runners/) | |
| 713 | + | is stopped the same way. | |
| 714 | + | ||
| 715 | + | ### Searching and downloading logs | |
| 716 | + | ||
| 717 | + | The **Search logs** box above a job's steps shows only the lines that hold | |
| 718 | + | what you type, in any case, with each match marked and every step that has | |
| 719 | + | one opened. Lines inside folded groups are searched too. | |
| 720 | + | ||
| 721 | + | | Download | Where | | |
| 722 | + | | --- | --- | | |
| 723 | + | | One job's whole log, as text | The download button beside the job's name. | | |
| 724 | + | | Every job's log of an attempt, as a zip | **Download logs** at the top of the run. The zip holds `1_<job>.txt` with each job's whole log, and a `<job>/` folder with `<step>_<step name>.txt` for each step. | | |
| 725 | + | ||
| 726 | + | A zip holds up to 24 MiB of logs; jobs past that are listed with a note | |
| 727 | + | to download them on their own. Each job keeps up to 4 MB of log. | |
| 728 | + | ||
| 729 | + | ### Status badges | |
| 730 | + | ||
| 731 | + | A badge shows how a workflow's latest finished run went: **passing**, | |
| 732 | + | **failing**, **cancelled**, or **no status** before it has finished one. | |
| 733 | + | ||
| 734 | + | 1. Open the repository's **Actions** page and pick the workflow. | |
| 735 | + | 2. Click **Create status badge**. | |
| 736 | + | 3. Choose a branch and an event, if you want them, and copy the Markdown. | |
| 737 | + | ||
| 738 | + | ```markdown | |
| 739 | + | [](https://g1t.sh/acme/web/actions?workflow=ci.yml) | |
| 740 | + | ``` | |
| 741 | + | ||
| 742 | + | The address is `https://g1t.sh/{workspace}/{repo}/actions/workflows/{file}/badge.svg`, | |
| 743 | + | where `{file}` is the workflow's file name in `.g1t/workflows/`. It takes: | |
| 744 | + | ||
| 745 | + | | Parameter | Shows | | |
| 746 | + | | --- | --- | | |
| 747 | + | | `branch` | Runs on that branch. Without it, the default branch's runs, or any branch's when the default branch has none. | | |
| 748 | + | | `event` | Runs started by that event, such as `push` or `pull_request`. | | |
| 749 | + | ||
| 750 | + | A public repository's badge loads for anyone and is cached for a minute. | |
| 751 | + | A private repository's loads only for someone who can see the repository, | |
| 752 | + | so it does not show in a README read anywhere else. | |
| 753 | + | ||
| 629 | 754 | ### Masking secrets | |
| 630 | 755 | ||
| 631 | 756 | Every secret's value is replaced with `***` wherever a job prints it, and | |
| ⋯ | |||
| 1000 | 1125 | | `list` | `GET /repos/{owner}/{repo}/actions/workflows` | | |
| 1001 | 1126 | | `list_runs` | `GET /repos/{owner}/{repo}/actions/runs`, with `workflow`, `branch`, `event`, `pull`, `head_sha` | | |
| 1002 | 1127 | | `get_run` | `GET /repos/{owner}/{repo}/actions/runs/{id}` | | |
| 1003 | − | | `job_logs` | `GET /repos/{owner}/{repo}/actions/jobs/{job}/logs?after=` | | |
| 1128 | + | | `job_logs` | `GET /repos/{owner}/{repo}/actions/jobs/{job}/logs?after=`, or `?format=text` for the whole log as plain text | | |
| 1129 | + | | `get_run` with `attempt` | `GET /repos/{owner}/{repo}/actions/runs/{id}/attempts/{attempt}` | | |
| 1130 | + | | No tool: a download | `GET /repos/{owner}/{repo}/actions/runs/{id}/logs`, or `…/attempts/{attempt}/logs`: every job's log as a zip | | |
| 1004 | 1131 | | `dispatch` | `POST /repos/{owner}/{repo}/actions/workflows/{workflow}/dispatches` with `ref` and `inputs` | | |
| 1005 | − | | `cancel` | `POST /repos/{owner}/{repo}/actions/runs/{id}/cancel` | | |
| 1006 | − | | `rerun` | `POST …/runs/{id}/rerun`, or `…/rerun-failed-jobs` | | |
| 1132 | + | | `cancel` | `POST /repos/{owner}/{repo}/actions/runs/{id}/cancel`; `…/force-cancel`, or `force`, to stop running jobs without their cleanup steps | | |
| 1133 | + | | `rerun` | `POST …/runs/{id}/rerun`, or `…/rerun-failed-jobs`; one job and those that need it with `POST /repos/{owner}/{repo}/actions/jobs/{job}/rerun`. Each takes `enable_debug_logging` (or `debug`) | | |
| 1007 | 1134 | | `update` | `PUT …/workflows/{workflow}/enable` and `…/disable` | | |
| 1008 | 1135 | | `approve_run` | `POST /repos/{owner}/{repo}/actions/runs/{id}/approve` | | |
| 1009 | 1136 | | `pending_deployments` | `GET /repos/{owner}/{repo}/actions/runs/{id}/pending_deployments` | | |
| 743 | 743 | inside other actions), downloads from other repositories, and npm | |
| 744 | 744 | trusted publishing, which depends on npm accepting g1t's issuer. | |
| 745 | 745 | ||
| 746 | + | ### Runs: summaries, attempts, cancelling, logs and badges (built 2026-10-08) | |
| 747 | + | ||
| 748 | + | GitHub Actions parity on the run itself: | |
| 749 | + | ||
| 750 | + | - [x] **Job summaries.** The runner sends each step's | |
| 751 | + | `$GITHUB_STEP_SUMMARY`, masked, as a `summary` report (1 MiB a step, | |
| 752 | + | 20 steps a job, kept in `job_summaries`, actions/0008); the run's page | |
| 753 | + | renders them as sanitised Markdown, a card per job. | |
| 754 | + | - [x] **Attempts and re-runs.** A re-run is a new attempt that keeps the | |
| 755 | + | one before (`run_attempts`, `job_attempts`; a re-run job's logs and | |
| 756 | + | summary move to `{job}.{attempt}`, which is its id when that attempt is | |
| 757 | + | viewed). Re-run all, failed, or one job (`POST …/jobs/{job}/rerun`) with | |
| 758 | + | the jobs that need it; a matrix or a called workflow runs again whole. | |
| 759 | + | Debug re-runs set `RUNNER_DEBUG=1`, `ACTIONS_STEP_DEBUG`, | |
| 760 | + | `ACTIONS_RUNNER_DEBUG` and `runner.debug`; the runner then shows | |
| 761 | + | `::debug::` lines and how each step's `if:` read. An attempt picker on | |
| 762 | + | the run's page, `attempt` on `get_workflow_run` and | |
| 763 | + | `…/runs/{id}/attempts/{n}`. | |
| 764 | + | - [x] **Graceful cancel.** A running job gets `cancel_requested_at`, | |
| 765 | + | told in the answer to its next report (a quiet step pings every 10 s): | |
| 766 | + | the step's process group gets SIGINT, SIGTERM at 7.5 s, SIGKILL at 10 s; | |
| 767 | + | then only `always()`/`cancelled()` steps and post steps run, and the job | |
| 768 | + | ends `cancelled`. Hard-stopped after 5 minutes; cancelling again or | |
| 769 | + | `…/force-cancel` stops at once. | |
| 770 | + | - [x] **Logs.** Search on the run's page (matching lines, groups opened); | |
| 771 | + | one job's log as text (`…/jobs/{job}/log.txt`, API | |
| 772 | + | `…/jobs/{job}/logs?format=text`) and an attempt's as a zip laid out as | |
| 773 | + | GitHub's (`…/runs/{id}/logs.zip`, API `…/runs/{id}/logs`), up to 24 MiB. | |
| 774 | + | - [x] **Status badges** at `/{ws}/{repo}/actions/workflows/{file}/badge.svg` | |
| 775 | + | with `branch` and `event`; public repositories' for anyone (cached a | |
| 776 | + | minute), private ones' only for viewers who can see them. "Create status | |
| 777 | + | badge" on the workflow's page gives the Markdown. | |
| 778 | + | - Not yet: re-running one combination of a matrix alone; signals into a | |
| 779 | + | job container's processes on cancel (they reach `docker exec` only). | |
| 780 | + | ||
| 746 | 781 | ## A repository that maintains itself | |
| 747 | 782 | ||
| 748 | 783 | > **2026-10-04:** the user asked for Dependabot, GitHub Advanced Security and |