Skip to content

Commit

Permalink
Merge branch 'docs-gardener'
Browse files Browse the repository at this point in the history
  • Loading branch information
rhysd committed Oct 20, 2024
2 parents 174d079 + abb249d commit d32b16e
Show file tree
Hide file tree
Showing 45 changed files with 1,390 additions and 143 deletions.
9 changes: 8 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,21 @@ jobs:
- uses: actions/setup-go@v5
with:
go-version: '1.23'
- run: sudo apt-get install -y shellcheck
- name: Install dependencies
run: |
sudo apt-get install -y shellcheck
pip install pyflakes
shellcheck --version
pyflakes --version
- name: Check Go sources are formatted
run: |
diffs="$(gofmt -d ./*.go ./cmd/actionlint/*.go ./scripts/*/*.go ./playground/*.go)"
if [[ "$diffs" != "" ]]; then
echo "$diffs" >&2
exit 1
fi
- name: Check `docs/checks.md` is up-to-date
run: go run ./scripts/update-checks-doc -check ./docs/checks.md
- name: Install staticcheck
run: |
go install honnef.co/go/tools/cmd/staticcheck@latest
Expand Down
17 changes: 16 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ To report a bug, please submit a new ticket on GitHub. It's helpful to search si
https://github.com/rhysd/actionlint/issues/new

Providing a reproducible workflow content is much appreciated. If only a small snippet of workflow is provided or no
input is provided at all, such issue tickets may get lower priority since they are occasionally time consuming to
input is provided at all, such issue tickets may get lower priority because they are occasionally time consuming to
investigate.

# Sending a patch
Expand All @@ -21,6 +21,10 @@ Before submitting your PR, please ensure the following points:
- Confirm build/tests/lints passed. How to run them is described in the following sections.
- If you added a new feature, consider to add tests and explain it in [the usage document](docs/usage.md).
- If you added a new public API, consider to add tests and a doc comment for the API.
- If you updated [the checks document](docs/checks.md), ensure to run [the maintenance script](#about-checks-doc).

Special thanks to the native English speakers for proofreading the documentation and error messages, as the author is not
proficient in English.

# Development

Expand Down Expand Up @@ -219,3 +223,14 @@ All tests are automated.
- `testdata/projects/` contains 'Project' tests. Each directories represent a single project (meaning a repository on GitHub).
Corresponding `*.out` files are expected error messages. Empty `*.out` file means the test case should cause no errors.
'Project' test is used for use cases where multiple files are related (reusable workflows, local actions, config files, ...).

<a id="about-checks-doc"></a>
## How to write checks document

The ['Checks' document](./docs/checks.md) is a large document to explain all checks by actionlint.

This document is maintained with [`update-checks-doc`](./scripts/update-checks-doc) script. This script automatically updates
the code blocks after `Output:` and the `Playground` links. This script should be run after modifying the document.

Please see [the readme of the script](./scripts/update-checks-doc/README.md) for the usage and knowing the details of the
document format that this script assumes.
247 changes: 133 additions & 114 deletions docs/checks.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ Note: The following targets are not tested since GitHub Actions doesn't support
- Windows i386, arm64
- FreeBSD i386, x86_64

<a name="download-script"></a>
<a id="download-script"></a>
## Download script

To install `actionlint` executable with one command, [the download script](../scripts/download-actionlint.bash) is available.
Expand Down
10 changes: 5 additions & 5 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ processes.
actionlint -shellcheck= -pyflakes=
```

<a name="format"></a>
<a id="format"></a>
### Format error messages

`-format` option can flexibly format error messages with [Go template syntax][go-template].
Expand Down Expand Up @@ -189,7 +189,7 @@ Note that special characters escaped with backslash like `\n` in the format stri
| `2` | The command failed due to invalid command line option |
| `3` | The command failed due to some fatal error |

<a name="on-github-actions"></a>
<a id="on-github-actions"></a>
## Use actionlint on GitHub Actions

Preparing `actionlint` executable with the download script is recommended. See [the instruction](install.md#download-script) for
Expand Down Expand Up @@ -263,7 +263,7 @@ Paste your workflow content to the code editor at left pane. It automatically sh
the workflow content in the code editor, the results will be updated on the fly. Clicking an error message in the results
table moves a cursor to position of the error in the code editor.

<a name="docker"></a>
<a id="docker"></a>
## [Docker][docker] image

[Official Docker image][docker-image] is available. The image contains `actionlint` executable and all dependencies (shellcheck
Expand Down Expand Up @@ -304,7 +304,7 @@ docker run --rm -v /path/to/workflows:/workflows rhysd/actionlint:latest -color
Go APIs are available. See [the Go API document](api.md) for more details.


<a name="tools-integ"></a>
<a id="tools-integ"></a>
## Tools integration

### reviewdog
Expand All @@ -325,7 +325,7 @@ jobs:
- uses: reviewdog/action-actionlint@v1
```

<a name="problem-matchers"></a>
<a id="problem-matchers"></a>
### Problem Matchers

[Problem Matchers][problem-matchers] is a feature to extract GitHub Actions annotations from terminal outputs of linters.
Expand Down
54 changes: 34 additions & 20 deletions rule_job_needs.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package actionlint

import (
"fmt"
"strconv"
"strings"
)

Expand Down Expand Up @@ -44,7 +44,7 @@ func NewRuleJobNeeds() *RuleJobNeeds {
}
}

func contains(heystack []string, needle string) bool {
func contains[T comparable](heystack []T, needle T) bool {
for _, s := range heystack {
if s == needle {
return true
Expand Down Expand Up @@ -107,47 +107,61 @@ func (rule *RuleJobNeeds) VisitWorkflowPost(n *Workflow) error {
return nil
}

if edge := detectCyclic(rule.nodes); edge != nil {
edges := map[string]string{}
edges[edge.from.id] = edge.to.id
collectCyclic(edge.to, edges)
// Note: Only the first cycle can be detected even if there are multiple cycles in "needs:" configurations.
if edge := detectFirstCycle(rule.nodes); edge != nil {
edges := map[*jobNode]*jobNode{}
edges[edge.from] = edge.to
collectCycle(edge.to, edges)

// Start cycle from the smallest position to make the error message deterministic
start := edge.from
for n := range edges {
if n.pos.IsBefore(start.pos) {
start = n
}
}

desc := make([]string, 0, len(edges))
for from, to := range edges {
desc = append(desc, fmt.Sprintf("%q -> %q", from, to))
var msg strings.Builder
msg.WriteString("cyclic dependencies in \"needs\" job configurations are detected. detected cycle is ")

msg.WriteString(strconv.Quote(start.id))
from, to := start, edges[start]
for {
msg.WriteString(" -> ")
msg.WriteString(strconv.Quote(to.id))
from, to = to, edges[to]
if from == start {
break
}
}

rule.Errorf(
edge.from.pos,
"cyclic dependencies in \"needs\" configurations of jobs are detected. detected cycle is %s",
strings.Join(desc, ", "),
)
rule.Error(start.pos, msg.String())
}

return nil
}

func collectCyclic(src *jobNode, edges map[string]string) bool {
func collectCycle(src *jobNode, edges map[*jobNode]*jobNode) bool {
for _, dest := range src.resolved {
if dest.status != nodeStatusActive {
continue
}
edges[src.id] = dest.id
if _, ok := edges[dest.id]; ok {
edges[src] = dest
if _, ok := edges[dest]; ok {
return true
}
if collectCyclic(dest, edges) {
if collectCycle(dest, edges) {
return true
}
delete(edges, src.id)
delete(edges, src)
}
return false
}

// Detect cyclic dependencies
// https://inzkyk.xyz/algorithms/depth_first_search/detecting_cycles/

func detectCyclic(nodes map[string]*jobNode) *edge {
func detectFirstCycle(nodes map[string]*jobNode) *edge {
for _, v := range nodes {
if v.status == nodeStatusNew {
if e := detectCyclicNode(v); e != nil {
Expand Down
1 change: 1 addition & 0 deletions scripts/update-checks-doc/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/update-checks-doc
68 changes: 68 additions & 0 deletions scripts/update-checks-doc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
update-checks-doc
=================

This is a script to maintain [the 'Checks' document](../../docs/checks.md).

This script does:

- update the outputs of the example inputs; the code blocks after `Output:` header
- update the links to the [playground](https://rhysd.github.io/actionlint/) for the example inputs

For making the implementation simple, this script does not support Windows. Please run this script
on Linux or macOS.

## Usage

Update the document. This command directly modifies the file.

```sh
go run ./scripts/update-checks-doc ./docs/checks.md
```

Check the document is up-to-date.

```sh
go run ./scripts/update-checks-doc -check ./docs/checks.md
```

The check is run on the [CI workflow](../../.github/workflows/ci.yaml).

## Format

The format of the check document is:

````markdown
<a id="some-id"></a>
## This is title of the check

Example input:

```yaml
This section is referred to generate the output and the playground link
```

Output:

```
This section will be auto-generated
```

[Playground](https://rhysd.github.io/actionlint/#THIS_HASH_PART_WILL_BE_AUTO_GENERATED)

Explanation for the check...
````

When you don't want to update the output by this script, put the comment as follows. This script
will ignore the code block. Instead you need to write the output in the code blcok manually.

```yaml
Output:
<!-- Skip update output -->
```

When you don't want to put a playground link for your example input, put the comment as follows
instead of the link. This script will not generate the hash for the playground link.

```yaml
<!-- Skip playground link -->
```
Loading

0 comments on commit d32b16e

Please sign in to comment.