Skip to content

Commit c5fb733

Browse files
authored
feat(linear): add Linear fixture data generator (CHAOS-246 part 1/2) (#15)
* feat(linear): add Terraform seeder scaffold for CHAOS-246 * feat(linear): add deterministic Linear seeder for CHAOS-246 * feat(linear): document Linear fixture seeding for CHAOS-246
1 parent cd7f3a4 commit c5fb733

6 files changed

Lines changed: 1133 additions & 0 deletions

File tree

linear/README.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Linear Demo Data Seeder
2+
3+
This module seeds deterministic Linear fixture data for Developer Health analytics. It mirrors the `atlassian/` fixture pattern: Terraform orchestrates a local Python seeder, while the seeder writes teams, projects, cycles, issues, comments, and `out/manifest.json` via the Linear GraphQL API.
4+
5+
## Terraform support
6+
7+
Linear does not maintain an official Terraform provider. A community provider exists, but its resource coverage and maintenance cadence may not match the fixture needs. This module therefore uses Terraform only as an orchestrator (`null_resource` + `local-exec`) and performs Linear writes through `seed/seed_linear.py`.
8+
9+
## Requirements
10+
11+
- Python 3.10+
12+
- Terraform (optional, for orchestration)
13+
- Linear personal API key with permission to create teams/projects/issues
14+
15+
## Environment variables
16+
17+
- `LINEAR_API_KEY` — required for real writes. Pass the raw API key in the `Authorization` header value; do not prefix it with `Bearer`.
18+
19+
## Dry run
20+
21+
Dry run is the default Terraform behavior and does not require `LINEAR_API_KEY`:
22+
23+
```bash
24+
python -m venv .venv
25+
source .venv/bin/activate
26+
pip install -r linear/seed/requirements.txt
27+
python linear/seed/seed_linear.py --dry-run
28+
```
29+
30+
The dry run creates `linear/out/manifest.json` with deterministic counts and samples but performs no API writes.
31+
32+
## Terraform usage
33+
34+
```bash
35+
cd linear
36+
terraform init
37+
terraform apply
38+
```
39+
40+
To perform real writes:
41+
42+
```hcl
43+
# terraform.tfvars
44+
linear_api_key = "lin_api_..."
45+
```
46+
47+
Optional toggles:
48+
49+
- `enable_issue_creation` (default `false`): when false, runs `--dry-run`
50+
- `enable_cycles` (default `true`): create two-week cycles for each seeded team
51+
- `enable_comments` (default `true`): create deterministic comments on a subset of issues
52+
- `monthly_issue_count` (default `0`): override issue volume per team per month
53+
- `assignee_emails`: optional Linear user emails for deterministic assignment
54+
- `provision_start_date` / `provision_end_date`: override the 24-month date range
55+
56+
## Direct seeder usage
57+
58+
```bash
59+
export LINEAR_API_KEY="lin_api_..."
60+
python linear/seed/seed_linear.py \
61+
--story linear/seed/story_map.yaml \
62+
--manifest linear/out/manifest.json \
63+
--seed dev-health-linear-demo
64+
```
65+
66+
## Idempotency and timestamps
67+
68+
The seeder uses stable hashes in issue titles (`[<external_id>]`) and checks for existing issues before writing. Linear does not support backdating issue creation timestamps through normal GraphQL mutations, so simulated historical dates are encoded in issue descriptions, due dates, cycles, and the manifest.
69+
70+
## Investment View themes
71+
72+
`seed/story_map.yaml` uses the canonical Investment View themes only:
73+
74+
- Feature Delivery
75+
- Operational / Support
76+
- Maintenance / Tech Debt
77+
- Quality / Reliability
78+
- Risk / Security

linear/main.tf

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
locals {
2+
dry_run_flag = var.enable_issue_creation ? "" : "--dry-run"
3+
comments_flag = var.enable_comments ? "" : "--disable-comments"
4+
cycles_flag = var.enable_cycles ? "" : "--disable-cycles"
5+
start_date_flag = var.provision_start_date != "" ? "--start-date ${var.provision_start_date}" : ""
6+
end_date_flag = var.provision_end_date != "" ? "--end-date ${var.provision_end_date}" : ""
7+
monthly_issue_flag = var.monthly_issue_count != 0 ? "--monthly-issue-count ${var.monthly_issue_count}" : ""
8+
assignees_flag = length(var.assignee_emails) > 0 ? "--assignees ${join(",", var.assignee_emails)}" : ""
9+
date_range_valid = var.provision_end_date == "" || var.provision_start_date != ""
10+
auth_valid = !var.enable_issue_creation || var.linear_api_key != ""
11+
}
12+
13+
resource "null_resource" "seed" {
14+
lifecycle {
15+
precondition {
16+
condition = local.date_range_valid
17+
error_message = "provision_start_date is required when provision_end_date is set."
18+
}
19+
precondition {
20+
condition = local.auth_valid
21+
error_message = "linear_api_key is required when enable_issue_creation is true."
22+
}
23+
}
24+
25+
triggers = {
26+
story_map_hash = filesha256("${path.module}/seed/story_map.yaml")
27+
script_hash = filesha256("${path.module}/seed/seed_linear.py")
28+
seed_hash = sha256(var.seed_string)
29+
batch_size = var.batch_size
30+
dry_run = tostring(!var.enable_issue_creation)
31+
start_date = var.provision_start_date
32+
end_date = var.provision_end_date
33+
monthly_issues = var.monthly_issue_count
34+
}
35+
36+
provisioner "local-exec" {
37+
command = join(" ", compact([
38+
"pip3 install -q -r ${path.module}/seed/requirements.txt &&",
39+
"mkdir -p ${path.module}/out &&",
40+
"python3",
41+
"${path.module}/seed/seed_linear.py",
42+
"--story", "${path.module}/seed/story_map.yaml",
43+
"--manifest", "${path.module}/out/manifest.json",
44+
"--seed", var.seed_string,
45+
"--batch-size", tostring(var.batch_size),
46+
local.start_date_flag,
47+
local.end_date_flag,
48+
local.monthly_issue_flag,
49+
local.assignees_flag,
50+
local.dry_run_flag,
51+
local.comments_flag,
52+
local.cycles_flag,
53+
]))
54+
55+
environment = {
56+
LINEAR_API_KEY = var.linear_api_key
57+
}
58+
}
59+
}

linear/seed/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
PyYAML==6.0.1
2+
requests==2.32.4

0 commit comments

Comments
 (0)