Skip to content

Commit 4d895a3

Browse files
cbleckerclaude
andcommitted
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>
1 parent 992a380 commit 4d895a3

6 files changed

Lines changed: 1092 additions & 0 deletions

File tree

.github/workflows/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/README.md

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