Skip to content

Commit 77a0806

Browse files
jberkuscbleckerclaude
authored
Add candidate bio validator (#120)
* Add candidate bio validation tool Adds a Go-based validator for Elekto candidate bio files, adapted from the Kubernetes community's verify-steering-election-tool.go. Features: - YAML frontmatter validation (name, ID, election-specific fields) - Filename/ID matching enforcement - Configurable word limits and required markdown sections - Comprehensive test suite with 100% passing tests - GitHub Actions workflow for automated CI testing The tool can be run directly from GitHub or built locally, making it easy for election administrators to validate candidate submissions. Co-Authored-By: Claude <noreply@anthropic.com> * Take Christoph's workflow for bio validation, and move it to the scripts folder so that it won't run against this repository. Signed-off-by: Josh Berkus <josh@agliodbs.com> --------- Signed-off-by: Josh Berkus <josh@agliodbs.com> Co-authored-by: Christoph Blecker <admin@toph.ca> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 17f6051 commit 77a0806

File tree

6 files changed

+1094
-0
lines changed

6 files changed

+1094
-0
lines changed

scripts/validate-bios/README.md

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
# Candidate Bio Validator
2+
3+
A Go-based validation tool for Elekto candidate bio files. This tool validates candidate bios against the Elekto format specification and configurable validation rules.
4+
5+
## Overview
6+
7+
Adapted from the [Kubernetes community's verify-steering-election-tool.go](https://github.com/kubernetes/community/blob/master/hack/verify-steering-election-tool.go), this generic validator checks candidate bio files for:
8+
9+
- Correct YAML frontmatter format
10+
- Required fields (name, ID, and election-specific info fields)
11+
- Filename/ID matching
12+
- Optional word count limits
13+
- Optional required markdown sections
14+
15+
## Installation
16+
17+
No installation required! Run directly from GitHub:
18+
19+
```bash
20+
go run github.com/elekto-io/elekto/scripts/validate-bios@latest <election-path>
21+
```
22+
23+
Or for local development, build from source:
24+
25+
```bash
26+
cd scripts/validate-bios
27+
go build -o validate-bios .
28+
```
29+
30+
This directory also includes a [sample Github workflow](go-test.yml), which can be added to repositories with election information in order to automatically test bios on merge.
31+
32+
## Usage
33+
34+
### Basic Validation
35+
36+
```bash
37+
# Run directly from GitHub (recommended)
38+
go run github.com/elekto-io/elekto/scripts/validate-bios@latest /path/to/election
39+
40+
# Or for local development
41+
cd scripts/validate-bios
42+
go run . /path/to/election
43+
44+
# Or with a built binary
45+
cd scripts/validate-bios
46+
./validate-bios /path/to/election
47+
```
48+
49+
### With Options
50+
51+
```bash
52+
# Enforce word limits
53+
go run github.com/elekto-io/elekto/scripts/validate-bios@latest \
54+
--max-words=450 \
55+
--recommended-words=300 \
56+
/path/to/election
57+
58+
# Require specific markdown sections
59+
go run github.com/elekto-io/elekto/scripts/validate-bios@latest \
60+
--required-sections="## About Me,## Platform,## Why I'm Running" \
61+
/path/to/election
62+
63+
# Combine all options
64+
go run github.com/elekto-io/elekto/scripts/validate-bios@latest \
65+
--max-words=450 \
66+
--recommended-words=300 \
67+
--required-sections="## About Me,## Platform" \
68+
/path/to/election
69+
```
70+
71+
## Command-Line Flags
72+
73+
| Flag | Type | Default | Description |
74+
|------|------|---------|-------------|
75+
| `--max-words` | int | 0 (no limit) | Maximum word count allowed in bio files |
76+
| `--recommended-words` | int | 0 (no limit) | Recommended word count (shown in error messages) |
77+
| `--required-sections` | string | "" | Comma-separated list of required markdown section headers |
78+
79+
## Candidate File Format
80+
81+
Candidate bios must follow the Elekto format:
82+
83+
```markdown
84+
-------------------------------------------------------------
85+
name: Candidate Full Name
86+
ID: github-username
87+
info:
88+
- employer: Company Name
89+
- slack: '@username'
90+
-------------------------------------------------------------
91+
92+
## About Me
93+
94+
Bio content here...
95+
96+
## Platform
97+
98+
Platform content here...
99+
```
100+
101+
### Format Specification
102+
103+
- **Start delimiter**: Exactly 61 dashes (`-`)
104+
- **End delimiter**: Three dashes (`---`)
105+
- **Required fields**:
106+
- `name`: Candidate's full name
107+
- `ID`: GitHub username (must match filename)
108+
- **Filename format**: `candidate-{ID}.md` where `{ID}` matches the `ID` field in the YAML header
109+
- **Info fields**: List of key-value pairs. Required fields are determined by `show_candidate_fields` in `election.yaml`
110+
111+
## Election Configuration Integration
112+
113+
The validator reads `election.yaml` from the election directory to determine which info fields are required:
114+
115+
```yaml
116+
# election.yaml
117+
name: 2025 Steering Committee Election
118+
start_datetime: 2025-01-01T00:00:00Z
119+
end_datetime: 2025-01-31T23:59:59Z
120+
show_candidate_fields:
121+
- employer
122+
- slack
123+
```
124+
125+
If `show_candidate_fields` is specified, the validator will check that each candidate's `info` section contains all listed fields.
126+
127+
## Validation Rules
128+
129+
1. **YAML Header Parsing**
130+
- Extract YAML between 61-dash start delimiter and `---` end delimiter
131+
- Validate `name` and `ID` fields exist
132+
133+
2. **Filename/ID Matching**
134+
- Filename must be `candidate-{ID}.md`
135+
- ID in filename must match `ID` field in YAML header
136+
137+
3. **Info Fields** (if `show_candidate_fields` is set in `election.yaml`)
138+
- Validate each listed field exists in the candidate's `info` section
139+
140+
4. **Word Count** (if `--max-words` flag is provided)
141+
- Count all words in the file
142+
- Error if count exceeds `--max-words`
143+
144+
5. **Required Sections** (if `--required-sections` flag is provided)
145+
- Check that bio content contains each specified section header
146+
147+
## Exit Codes
148+
149+
- `0` - All candidate bios validated successfully
150+
- `1` - One or more validation errors detected
151+
152+
## Testing
153+
154+
Run the test suite:
155+
156+
```bash
157+
cd scripts/validate-bios
158+
go test -v
159+
```
160+
161+
## Example Output
162+
163+
### Successful Validation
164+
165+
```
166+
All 5 candidate bio(s) validated successfully.
167+
```
168+
169+
### Failed Validation
170+
171+
```
172+
/path/to/election/candidate-user1.md: has 475 words
173+
/path/to/election/candidate-user2.md: missing required info field: employer
174+
/path/to/election/candidate-user3.md: filename username 'user3' does not match ID 'user-3' in header
175+
176+
====================================================================
177+
3 invalid candidate bio(s) detected.
178+
Bios should be limited to around 300 words, excluding headers.
179+
Bios must follow the nomination template and filename format.
180+
====================================================================
181+
```
182+
183+
## License
184+
185+
Copyright 2025 The Elekto Authors
186+
187+
Licensed under the Apache License, Version 2.0 (the "License");
188+
you may not use this file except in compliance with the License.
189+
You may obtain a copy of the License at
190+
191+
http://www.apache.org/licenses/LICENSE-2.0
192+
193+
Unless required by applicable law or agreed to in writing, software
194+
distributed under the License is distributed on an "AS IS" BASIS,
195+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
196+
See the License for the specific language governing permissions and
197+
limitations under the License.
198+
199+
## Source
200+
201+
Adapted from [kubernetes/community verify-steering-election-tool.go](https://github.com/kubernetes/community/blob/master/hack/verify-steering-election-tool.go)

scripts/validate-bios/go-test.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Go tests
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- 'scripts/validate-bios/**'
7+
push:
8+
branches:
9+
- main
10+
paths:
11+
- 'scripts/validate-bios/**'
12+
13+
jobs:
14+
test:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v5
18+
19+
- uses: actions/setup-go@v5
20+
with:
21+
go-version: stable
22+
23+
- name: Run tests
24+
working-directory: scripts/validate-bios
25+
run: go test -v ./...

scripts/validate-bios/go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module github.com/elekto-io/elekto/scripts/validate-bios
2+
3+
go 1.22
4+
5+
require gopkg.in/yaml.v3 v3.0.1

scripts/validate-bios/go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
3+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
4+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)