Skip to content

Commit 31fabb8

Browse files
authored
Merge pull request #8 from AOSSIE-Org/chore/project-consistency-docs-ci
Align scoring configs, add docs, CI workflow, and related fixes
2 parents 3df260e + 26f8d7d commit 31fabb8

11 files changed

Lines changed: 217 additions & 65 deletions

File tree

.github/workflows/sync-pr-labels.yml

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -199,46 +199,61 @@ jobs:
199199
pull_number: prNumber
200200
});
201201
202-
const additions = pr.data.additions;
203-
const deletions = pr.data.deletions;
202+
const additions = pr.data.additions ?? 0;
203+
const deletions = pr.data.deletions ?? 0;
204204
const totalChanges = additions + deletions;
205205
206206
console.log(`PR has ${additions} additions and ${deletions} deletions (${totalChanges} total changes)`);
207207
208208
// Determine size label based on total changes
209209
let sizeLabel = '';
210-
if (totalChanges <= 10) {
210+
if (Number.isFinite(totalChanges) && totalChanges <= 10) {
211211
sizeLabel = 'size/XS';
212-
} else if (totalChanges <= 50) {
212+
} else if (Number.isFinite(totalChanges) && totalChanges <= 50) {
213213
sizeLabel = 'size/S';
214-
} else if (totalChanges <= 200) {
214+
} else if (Number.isFinite(totalChanges) && totalChanges <= 200) {
215215
sizeLabel = 'size/M';
216-
} else if (totalChanges <= 500) {
216+
} else if (Number.isFinite(totalChanges) && totalChanges <= 500) {
217217
sizeLabel = 'size/L';
218-
} else {
218+
} else if (Number.isFinite(totalChanges)) {
219219
sizeLabel = 'size/XL';
220220
}
221221
222+
if (!sizeLabel) {
223+
console.log('Skipping size label: could not compute change stats');
224+
return;
225+
}
226+
222227
console.log(`Applying size label: ${sizeLabel}`);
223228
224229
// Remove any existing size labels first
225230
const currentLabels = await github.rest.issues.listLabelsOnIssue({
226231
owner: context.repo.owner,
227232
repo: context.repo.repo,
228-
issue_number: prNumber
233+
issue_number: prNumber,
234+
per_page: 100
229235
});
230236
231237
const sizeLabelsToRemove = currentLabels.data
232238
.map(label => label.name)
233239
.filter(name => name.startsWith('size/'));
234240
235241
for (const label of sizeLabelsToRemove) {
236-
await github.rest.issues.removeLabel({
237-
owner: context.repo.owner,
238-
repo: context.repo.repo,
239-
issue_number: prNumber,
240-
name: label
241-
});
242+
try {
243+
await github.rest.issues.removeLabel({
244+
owner: context.repo.owner,
245+
repo: context.repo.repo,
246+
issue_number: prNumber,
247+
name: label
248+
});
249+
} catch (e) {
250+
const status = e.status ?? e.response?.status;
251+
if (status === 404) {
252+
console.log(`Label already absent, skipping remove: ${label}`);
253+
} else {
254+
throw e;
255+
}
256+
}
242257
}
243258
244259
// Apply the new size label

.github/workflows/tests.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Python Tests
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- main
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
pytest:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v4
18+
19+
- name: Setup Python
20+
uses: actions/setup-python@v5
21+
with:
22+
python-version: "3.11"
23+
24+
- name: Install dependencies
25+
run: |
26+
python -m pip install --upgrade pip
27+
python -m pip install -e .[dev]
28+
29+
- name: Run tests
30+
run: pytest

CONTRIBUTING.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Contributing to Gitcord
2+
3+
Thanks for your interest in improving Gitcord.
4+
5+
## Setup
6+
7+
1. Fork and clone the repository.
8+
2. Create and activate a virtual environment.
9+
3. Install project dependencies (runtime + dev extras: `pytest`, `ruff`):
10+
- `./.venv/bin/python -m pip install -e ".[dev]"`
11+
12+
## Development Workflow
13+
14+
1. Create a feature branch from `main`.
15+
2. Make focused changes with clear commit messages.
16+
3. Run tests locally:
17+
- `pytest`
18+
4. Open a pull request with:
19+
- What changed
20+
- Why it changed
21+
- How it was tested
22+
23+
## Coding Guidelines
24+
25+
- Keep changes small and scoped.
26+
- Preserve offline-first and audit-first behavior.
27+
- Prefer deterministic logic in planning/scoring paths.
28+
- Update docs/config examples when behavior changes.
29+
30+
## Pull Request Checklist
31+
32+
- [ ] Tests pass locally
33+
- [ ] Docs updated when needed
34+
- [ ] Config examples still match runtime behavior
35+
- [ ] No secrets committed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ Wait 30 seconds for commands to sync.
219219
2. **Live role updates:** To have the bot actually add/remove roles in Discord, set in your config:
220220
- `runtime.mode: "active"`
221221
- `discord.permissions.write: true`
222-
Then run `run-once` again. Ensure the bot’s role in the server is **above** any roles it should assign (Server Settings → Roles). See [Testing in Discord](docs/TESTING_DISCORD.md) for details.
222+
Then run `run-once` again. Ensure the bot’s role in the server is **above** any roles it should assign (Server Settings → Roles). See [Testing in Discord](docs/TESTING_DISCORD.md) for details.
223223

224224
---
225225

@@ -257,7 +257,7 @@ Not applicable (CLI automation engine).
257257

258258
## 🙌 Contributing
259259

260-
Thank you for considering contributing to this project! Contributions are highly appreciated and welcomed. To ensure smooth collaboration, please refer to our [Contribution Guidelines](./CONTRIBUTING.md).
260+
Thank you for considering contributing to this project! Contributions are highly appreciated and welcomed. To ensure smooth collaboration, please refer to our [Contribution Guidelines](CONTRIBUTING.md).
261261

262262
---
263263

config/aussie.yaml

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,43 @@
33
# For Docker: set data_dir to "/data" and use: docker compose run --rm bot --config /app/config/aussie.yaml bot
44

55
runtime:
6-
mode: "dry-run"
6+
mode: "active"
77
log_level: "INFO"
88
data_dir: "./data/aussie"
99
github_adapter: "ghdcbot.adapters.github.rest:GitHubRestAdapter"
1010
discord_adapter: "ghdcbot.adapters.discord.api:DiscordApiAdapter"
1111
storage_adapter: "ghdcbot.adapters.storage.sqlite:SqliteStorage"
12+
# Notifications still run; these only skip score computation and Discord role add/remove.
13+
enable_scoring: false
14+
enable_discord_role_updates: false
1215

1316
github:
1417
org: "AOSSIE-Org"
1518
token: "${GITHUB_TOKEN}"
1619
api_base: "https://api.github.com"
1720
permissions:
1821
read: true
19-
write: false
22+
write: true
2023
user_fallback: false
2124

2225
discord:
2326
guild_id: "1022871757289422898"
2427
token: "${DISCORD_TOKEN}"
2528
permissions:
2629
read: true
27-
write: false
30+
write: true
2831
activity_channel_id: null
2932
pr_preview_channels: []
33+
# Verified GitHub → Discord DMs (or channel if channel_id is set). Requires linked users (/link + /verify-link).
34+
notifications:
35+
enabled: true
36+
issue_assignment: true
37+
pr_review_requested: true
38+
pr_review_result: true
39+
pr_merged: true
40+
coderabbit_reminders: false
41+
coderabbit_reminder_after_hours: 48
42+
channel_id: null # null = DM; set to a channel ID to post there instead
3043

3144
scoring:
3245
period_days: 30
@@ -51,4 +64,3 @@ assignments:
5164

5265
repo_contributor_roles: {}
5366

54-
identity_mappings: []

config/docker-example.yaml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,8 @@ discord:
3030
scoring:
3131
period_days: 30
3232
weights:
33-
issue_opened: 3
34-
pr_opened: 5
35-
pr_reviewed: 2
36-
comment: 1
33+
# Base scoring currently uses merged PR events.
34+
pr_merged: 5
3735

3836
role_mappings:
3937
- discord_role: "Contributor"

config/example.yaml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,8 @@ discord:
4848
scoring:
4949
period_days: 30
5050
weights:
51-
issue_opened: 3
52-
pr_opened: 5
53-
pr_reviewed: 2
54-
comment: 1
51+
# Base scoring currently uses merged PR events.
52+
pr_merged: 5
5553

5654
role_mappings:
5755
- discord_role: "Contributor"

docs/TESTING_DISCORD.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Testing in Discord
2+
3+
This guide explains how to safely validate Gitcord role automation in a Discord server.
4+
5+
## Recommended Test Sequence
6+
7+
1. **Dry-run phase (no Discord role mutations):** In your config, set:
8+
- `runtime.mode: "dry-run"`
9+
- `runtime.enable_discord_role_updates: false`
10+
- `discord.permissions.write: false`
11+
With `enable_discord_role_updates: false`, `run-once` will not apply Discord role add/remove even if mode or permissions were mis-set.
12+
2. Run a sync:
13+
- `python -m ghdcbot.cli --config config/my-org-config.yaml run-once`
14+
3. Review the generated report at `<data_dir>/reports/audit.md`.
15+
4. Verify planned role changes and identity mappings are correct.
16+
5. **Live phase (apply role updates):** Only after review, set:
17+
- `runtime.mode: "active"`
18+
- `runtime.enable_discord_role_updates: true`
19+
- `discord.permissions.write: true`
20+
6. Run `run-once` again and confirm expected role changes in Discord.
21+
22+
## Discord Permission Checklist
23+
24+
- Bot has `Manage Roles`, `View Channels`, `Send Messages`, `Embed Links`, and `Read Message History`.
25+
- Bot role is above any role it should assign/remove.
26+
- Application has `Server Members Intent` enabled in Discord Developer Portal.
27+
28+
## Bot Command Smoke Tests
29+
30+
- Identity: `/link`, `/verify-link`, `/verify`, `/status`, `/unlink`
31+
- Metrics: `/summary`, `/pr-info`
32+
- Mentor actions (with configured role): `/request-issue`, `/assign-issue`, `/issue-requests`, `/sync`
33+
34+
If slash commands do not appear immediately, wait for command sync and ensure the configured `discord.guild_id` is correct.

src/ghdcbot/bot.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,14 +1726,19 @@ async def sync_cmd(interaction: discord.Interaction) -> None:
17261726
)
17271727

17281728
await interaction.followup.send("🔄 Syncing GitHub events and sending notifications...", ephemeral=True)
1729-
1730-
# Run orchestrator (this will ingest and send notifications)
1731-
orchestrator.run_once()
1732-
1729+
1730+
# run_once() is synchronous and can take many minutes for large orgs; running it on the
1731+
# event loop blocks Discord heartbeats and delays other slash commands (they then hit
1732+
# "application did not respond"). Run it in a worker thread instead.
1733+
def _run_sync_and_close() -> None:
1734+
try:
1735+
orchestrator.run_once()
1736+
finally:
1737+
orchestrator.close()
1738+
1739+
await asyncio.to_thread(_run_sync_and_close)
1740+
17331741
await interaction.followup.send("✅ Sync complete! Notifications sent for new GitHub events.", ephemeral=True)
1734-
1735-
# Cleanup
1736-
orchestrator.close()
17371742
except Exception as exc:
17381743
logger.exception("Sync failed", exc_info=True)
17391744
await interaction.followup.send(

src/ghdcbot/config/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ class RuntimeConfig(BaseModel):
3636
github_adapter: str
3737
discord_adapter: str
3838
storage_adapter: str
39+
# When false, skip score computation/upsert (ingestion and notifications still run).
40+
enable_scoring: bool = True
41+
# When false, skip applying Discord role add/remove (notifications unaffected).
42+
enable_discord_role_updates: bool = True
3943

4044
@field_validator("log_level")
4145
@classmethod

0 commit comments

Comments
 (0)