Skip to content

Commit de63aa9

Browse files
fredbiclaude
andauthored
doc: add portable agentic instructions (#114)
Add contributions rule, GitHub workflows conventions rule, AGENTS.md symlink, update copilot-instructions.md. Signed-off-by: Frederic BIDON <fredbi@yahoo.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 97319b8 commit de63aa9

File tree

5 files changed

+352
-1
lines changed

5 files changed

+352
-1
lines changed

.claude/rules/contributions.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
paths:
3+
- "**/*"
4+
---
5+
6+
# Contribution rules (go-openapi)
7+
8+
Read `.github/CONTRIBUTING.md` before opening a pull request.
9+
10+
## Commit hygiene
11+
12+
- Every commit **must** be DCO signed-off (`git commit -s`) with a real email address.
13+
PGP-signed commits are appreciated but not required.
14+
- Agents may be listed as co-authors (`Co-Authored-By:`) but the commit **author must be the human sponsor**.
15+
We do not accept commits solely authored by bots or agents.
16+
- Squash commits into logical units of work before requesting review (`git rebase -i`).
17+
18+
## Linting
19+
20+
Before pushing, verify your changes pass linting against the base branch:
21+
22+
```sh
23+
golangci-lint run --new-from-rev master
24+
```
25+
26+
Install the latest version if you don't have it:
27+
28+
```sh
29+
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
30+
```
31+
32+
## Problem statement
33+
34+
- Clearly describe the problem the PR solves, or reference an existing issue.
35+
- PR descriptions must not be vague ("fix bug", "improve code") — explain *what* was wrong and *why* the change is correct.
36+
37+
## Tests are mandatory
38+
39+
- Every bug fix or feature **must** include tests that demonstrate the problem and verify the fix.
40+
- The only exceptions are documentation changes and typo fixes.
41+
- Aim for at least 80% coverage of your patch.
42+
- Run the full test suite before submitting:
43+
44+
For mono-repos:
45+
```sh
46+
go test work ./...
47+
```
48+
49+
For single module repos:
50+
```sh
51+
go test ./...
52+
```
Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
---
2+
paths:
3+
- ".github/workflows/**.yml"
4+
- ".github/workflows/**.yaml"
5+
---
6+
7+
# GitHub Actions Workflows Formatting and Style Conventions
8+
9+
This rule captures YAML and bash formatting rules to provide a consistent maintainer's experience across CI workflows.
10+
11+
## File Structure
12+
13+
**REQUIRED**: All github action workflows are organized as a flat structure beneath `.github/workflows/`.
14+
15+
> GitHub does not support a hierarchical organization for workflows yet.
16+
17+
**REQUIRED**: YAML files are conventionally named `{workflow}.yml`, with the `.yml` extension.
18+
19+
## Code Style & Formatting
20+
21+
### Expression Spacing
22+
23+
**REQUIRED**: All GitHub Actions expressions must have spaces inside the braces:
24+
25+
```yaml
26+
# ✅ CORRECT
27+
env:
28+
PR_URL: ${{ github.event.pull_request.html_url }}
29+
TOKEN: ${{ secrets.GITHUB_TOKEN }}
30+
31+
# ❌ WRONG
32+
env:
33+
PR_URL: ${{github.event.pull_request.html_url}}
34+
TOKEN: ${{secrets.GITHUB_TOKEN}}
35+
```
36+
37+
> Provides a consistent formatting rule.
38+
39+
### Conditional Syntax
40+
41+
**REQUIRED**: Always use `${{ }}` in `if:` conditions:
42+
43+
```yaml
44+
# ✅ CORRECT
45+
if: ${{ inputs.enable-signing == 'true' }}
46+
if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }}
47+
48+
# ❌ WRONG (works but inconsistent)
49+
if: inputs.enable-signing == 'true'
50+
```
51+
52+
> Provides a consistent formatting rule.
53+
54+
### GitHub Workflow Commands
55+
56+
**REQUIRED**: Use workflow commands for status messages that should appear as annotations, with **double colon separator**:
57+
58+
```bash
59+
# ✅ CORRECT - Double colon (::) separator after title
60+
echo "::notice title=build::Build completed successfully"
61+
echo "::warning title=race-condition::Merge already in progress"
62+
echo "::error title=deployment::Failed to deploy"
63+
64+
# ❌ WRONG - Single colon separator (won't render as annotation)
65+
echo "::notice title=build:Build completed" # Missing second ':'
66+
echo "::warning title=x:message" # Won't display correctly
67+
```
68+
69+
**Syntax pattern:** `::LEVEL title=TITLE::MESSAGE`
70+
- `LEVEL`: notice, warning, or error
71+
- Double `::` separator is required between title and message
72+
73+
> Wrong syntax may raise untidy warnings and produce botched output.
74+
75+
### YAML arrays formatting
76+
77+
For steps, YAML arrays are formatted with the following indentation:
78+
79+
```yaml
80+
# ✅ CORRECT - Clear spacing between steps
81+
steps:
82+
-
83+
name: Dependabot metadata
84+
id: metadata
85+
uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0
86+
-
87+
name: Checkout repository
88+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
89+
with:
90+
fetch-depth: 0
91+
92+
# ❌ WRONG - Dense format, more difficult to read
93+
steps:
94+
- name: Dependabot metadata
95+
id: metadata
96+
uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0
97+
- name: Checkout repository
98+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
99+
with:
100+
fetch-depth: 0
101+
102+
# ❌ WRONG - YAML comment or blank line could be avoided
103+
steps:
104+
#
105+
- name: Dependabot metadata
106+
id: metadata
107+
uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0
108+
109+
- name: Checkout repository
110+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
111+
with:
112+
fetch-depth: 0
113+
```
114+
115+
## Security Best Practices
116+
117+
### Version Pinning using SHAs
118+
119+
**REQUIRED**: Always pin action versions to commit SHAs:
120+
121+
> Runs must be repeatable with known pinned version. Automated updates are pushed frequently (e.g. daily or weekly)
122+
> to keep pinned versions up-to-date.
123+
124+
```yaml
125+
# ✅ CORRECT - Pinned to commit SHA with version comment
126+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
127+
uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6.3.0
128+
129+
# ❌ WRONG - Mutable tag reference
130+
uses: actions/checkout@v6
131+
```
132+
133+
### Permission settings
134+
135+
**REQUIRED**: Always set minimal permissions at the workflow level.
136+
137+
```yaml
138+
# ✅ CORRECT - Workflow level permissions set to minimum
139+
permissions:
140+
contents: read
141+
142+
# ❌ WRONG - Workflow level permissions with undue privilege escalation
143+
permissions:
144+
contents: write
145+
pull-requests: write
146+
```
147+
148+
**REQUIRED**: Whenever a job needs elevated privileges, always raise required permissions at the job level.
149+
150+
```yaml
151+
# ✅ CORRECT - Job level permissions set to the specific requirements for that job
152+
jobs:
153+
dependabot:
154+
permissions:
155+
contents: write
156+
pull-requests: write
157+
uses: ./.github/workflows/auto-merge.yml
158+
secrets: inherit
159+
160+
# ❌ WRONG - Same permissions but set at workflow level instead of job level
161+
permissions:
162+
contents: write
163+
pull-requests: write
164+
```
165+
166+
> (Security best practice detected by CodeQL analysis)
167+
168+
### Undue secret exposure
169+
170+
**NEVER** use `secrets[inputs.name]` — always use explicit secret parameters.
171+
172+
> Using keyed access to secrets forces the runner to expose ALL secrets to the job, which causes a security risk
173+
> (caught and reported by CodeQL security analysis).
174+
175+
```yaml
176+
# ❌ SECURITY VULNERABILITY
177+
# This exposes ALL organization and repository secrets to the runner
178+
on:
179+
workflow_call:
180+
inputs:
181+
secret-name:
182+
type: string
183+
jobs:
184+
my-job:
185+
steps:
186+
- uses: some-action@v1
187+
with:
188+
token: ${{ secrets[inputs.secret-name] }} # ❌ DANGEROUS!
189+
```
190+
191+
**SOLUTION**: Use explicit secret parameters with fallback for defaults:
192+
193+
```yaml
194+
# ✅ SECURE
195+
on:
196+
workflow_call:
197+
secrets:
198+
gpg-private-key:
199+
required: false
200+
jobs:
201+
my-job:
202+
steps:
203+
- uses: go-openapi/gh-actions/ci-jobs/bot-credentials@master
204+
with:
205+
# Falls back to go-openapi default if not explicitly passed
206+
gpg-private-key: ${{ secrets.gpg-private-key || secrets.CI_BOT_GPG_PRIVATE_KEY }}
207+
```
208+
209+
## Common Gotchas
210+
211+
### Description fields containing parsable expressions
212+
213+
**REQUIRED**: **DO NOT** use `${{ }}` expressions in description fields:
214+
215+
> They may be parsed by the runner, wrongly interpreted or causing failure (e.g. "not defined in this context").
216+
217+
```yaml
218+
# ❌ WRONG - Can cause YAML parsing errors
219+
description: |
220+
Pass it as: gpg-private-key: ${{ secrets.MY_KEY }}
221+
222+
# ✅ CORRECT
223+
description: |
224+
Pass it as: secrets.MY_KEY
225+
```
226+
227+
### Boolean inputs
228+
229+
**Boolean inputs are forbidden**: NEVER use `type: boolean` for workflow inputs due to unpredictable type coercion
230+
231+
> gh-action expressions using boolean job inputs are hard to predict and come with many quirks.
232+
233+
```yaml
234+
# ❌ FORBIDDEN - Boolean inputs have type coercion issues
235+
on:
236+
workflow_call:
237+
inputs:
238+
enable-feature:
239+
type: boolean # ❌ NEVER USE THIS
240+
default: true
241+
242+
# The pattern `x == 'true' || x == true` seems safe but fails when:
243+
# - x is not a boolean: `x == true` evaluates to true if x != null
244+
# - Type coercion is unpredictable and error-prone
245+
246+
# ✅ CORRECT - Always use string type for boolean-like inputs
247+
on:
248+
workflow_call:
249+
inputs:
250+
enable-feature:
251+
type: string # ✅ Use string instead
252+
default: 'true' # String value
253+
254+
jobs:
255+
my-job:
256+
# Simple, reliable comparison
257+
if: ${{ inputs.enable-feature == 'true' }}
258+
259+
# ✅ In bash, this works perfectly (inputs are always strings in bash):
260+
if [[ '${{ inputs.enable-feature }}' == 'true' ]]; then
261+
echo "Feature enabled"
262+
fi
263+
```
264+
265+
**Rule**: Use `type: string` with values `'true'` or `'false'` for all boolean-like workflow inputs.
266+
267+
**Note**: Step outputs and bash variables are always strings, so `x == 'true'` works fine for those.
268+
269+
### YAML fold scalars in action inputs
270+
271+
**NEVER** use `>` or `>-` (fold scalars) for `with:` input values:
272+
273+
> The YAML spec says fold scalars replace newlines with spaces, but the GitHub Actions runner
274+
> does not reliably honor this for action inputs. The action receives the literal multi-line string
275+
> instead of a single folded line, which breaks flag parsing.
276+
277+
```yaml
278+
# ❌ BROKEN - Fold scalar, args received with embedded newlines
279+
- uses: goreleaser/goreleaser-action@...
280+
with:
281+
args: >-
282+
release
283+
--clean
284+
--release-notes /tmp/notes.md
285+
286+
# ✅ CORRECT - Single line
287+
- uses: goreleaser/goreleaser-action@...
288+
with:
289+
args: release --clean --release-notes /tmp/notes.md
290+
291+
# ✅ CORRECT - Literal block scalar (|) is fine for run: scripts
292+
- run: |
293+
echo "line 1"
294+
echo "line 2"
295+
```
296+
297+
**Rule**: Use single-line strings for `with:` inputs. Only use `|` (literal block scalar) for `run:` scripts where multi-line is intentional.

.claude/rules/go-conventions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ paths:
88
- All files must have SPDX license headers (Apache-2.0).
99
- Go version policy: support the 2 latest stable Go minor versions.
1010
- Commits require DCO sign-off (`git commit -s`).
11+
- use `golangci-lint fmt` to format code (not `gofmt` or `gofumpt`)

.github/copilot-instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@ Custom types can implement `JSONPointable` (for Get) or `JSONSetable` (for Set)
4444
- Tests: `go test ./...` with `-race`. CI runs on `{ubuntu, macos, windows} x {stable, oldstable}`.
4545
- Test framework: `github.com/go-openapi/testify/v2` (not `stretchr/testify`).
4646

47-
See `.github/copilot/` (symlinked to `.claude/rules/`) for detailed rules on Go conventions, linting, and testing.
47+
See `.github/copilot/` (symlinked to `.claude/rules/`) for detailed rules on Go conventions, linting, testing, and contributions.

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.github/copilot-instructions.md

0 commit comments

Comments
 (0)