Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# GitHub Workflows

## Database Team PR Automation

**File:** `database-projects-pr-workflow.yml`

This workflow automatically updates the Database Team's GitHub Project board based on PR events.

### How it works

The workflow triggers on PR events (opened, review requested, review submitted, merged, etc.) and automatically:

1. **Checks domain labels** - Only processes PRs with labels starting with `domain::database` or `domain::core`
2. **Adds PR to project** - Ensures the PR is added to the Database Team project board
3. **Updates Status field** - Sets the PR status based on its state:
- `In Progress` - PR is a draft
- `In Review` - PR is ready for review
- `Done` - PR has been merged
4. **Updates Review Status field** - Sets the review status based on review state:
- `Awaiting` - Set when:
- PR switches from draft to ready for review
- There are pending review requests
- A reviewer who requested changes has been re-requested for review
- No reviewers have been requested yet and no approvals exist
- `Feedback` - Changes have been requested by a reviewer
- `Merge` - No pending review requests and at least one approval exists
5. **Updates linked issues** - Also updates the Status field (not Review Status) of any issues linked to the PR. This assumes a 1:1 relation between PRs and issues.

### State transitions

```
Draft PR opened/converted to draft
→ Status: In Progress
→ Review Status: (cleared)

PR ready for review / review requested
→ Status: In Review
→ Review Status: Awaiting

Changes requested
→ Status: In Review
→ Review Status: Feedback

Changes requested reviewer re-requested for review
→ Status: In Review
→ Review Status: Awaiting

PR approved
→ Status: In Review
→ Review Status: Merge

PR merged
→ Status: Done
→ Review Status: (cleared)
```

### Configuration

The workflow uses these environment variables (defined at the top of the file):

| Variable | Description |
|----------|-------------|
| `PROJECT_NUMBER` | The GitHub Project number (currently `3`) |
| `DOMAIN_LABELS` | Labels that trigger the workflow (`domain::database`, `domain::core`) |
| `STATUS_FIELD_NAME` | Name of the Status field in the project |
| `REVIEW_STATUS_FIELD_NAME` | Name of the Review Status field in the project |

### Requirements

- A GitHub token with project write permissions stored as `DATABASE_PROJECT_WORKFLOW_TOKEN` secret
- The project must have `Status` and `Review Status` single-select fields with the expected options

### Manual trigger

You can manually trigger the workflow for a specific PR using the "Run workflow" button in the Actions tab, providing the PR number.

---

## Database Team Issue Automation

**File:** `database-projects-issues-workflow.yml`

This workflow automatically adds new issues to the Database Team's GitHub Project board.

### How it works

The workflow triggers when an issue is opened or labeled, and:

1. **Checks domain labels** - Only processes issues with labels starting with `domain::database` or `domain::core`
2. **Checks if already in project** - Skips if the issue is already on the project board
3. **Adds issue to project** - Adds the issue to the Database Team project board
4. **Sets Status to Todo** - Sets the initial status to `Todo`

### Configuration

| Variable | Description |
|----------|-------------|
| `PROJECT_NUMBER` | The GitHub Project number (currently `3`) |
| `DOMAIN_LABELS` | Labels that trigger the workflow (`domain::database`, `domain::core`) |
| `STATUS_FIELD_NAME` | Name of the Status field in the project |
| `STATUS_TODO` | The initial status value for new issues (`Todo`) |

### Requirements

- A GitHub token with project write permissions stored as `DATABASE_PROJECT_WORKFLOW_TOKEN` secret
- The project must have a `Status` single-select field with a `Todo` option

### Manual trigger

You can manually trigger the workflow for a specific issue using the "Run workflow" button in the Actions tab, providing the issue number.
163 changes: 163 additions & 0 deletions .github/workflows/database-projects-issues-workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
name: Update Database Team Project Fields

env:
PROJECT_NUMBER: '3'
DOMAIN_LABELS: '["domain::database", "domain::core"]'
STATUS_FIELD_NAME: 'Status'
STATUS_TODO: 'Todo'

on:
issues:
types: [labeled, opened]
workflow_dispatch:
inputs:
issue_number:
description: 'Issue number to process'
required: true
type: number

jobs:
update-project-fields:
runs-on: ubuntu-latest
steps:
- name: Add Issue to Project
uses: actions/github-script@v7
with:
github-token: ${{ secrets.DATABASE_PROJECT_WORKFLOW_TOKEN }}
script: |
const issueNumber = context.eventName === 'workflow_dispatch'
? ${{ github.event.inputs.issue_number || 0 }}
: context.payload.issue.number;

console.log(`Processing Issue #${issueNumber} (event: ${context.eventName}/${context.payload.action || 'manual'})`);

// ============================================================
// FETCH ALL DATA WITH SINGLE GRAPHQL QUERY
// ============================================================

const data = await github.graphql(`
query($owner: String!, $repo: String!, $issue: Int!, $projectNumber: Int!) {
organization(login: $owner) {
projectV2(number: $projectNumber) {
id
fields(first: 20) {
nodes {
... on ProjectV2SingleSelectField {
id
name
options { id, name }
}
}
}
}
}
repository(owner: $owner, name: $repo) {
issue(number: $issue) {
id
labels(first: 20) {
nodes { name }
}
projectItems(first: 10) {
nodes {
id
project { id }
}
}
}
}
}
`, {
owner: context.repo.owner,
repo: context.repo.repo,
issue: issueNumber,
projectNumber: parseInt('${{ env.PROJECT_NUMBER }}')
});

const project = data.organization.projectV2;
const issue = data.repository.issue;

// ============================================================
// CHECK DOMAIN LABELS (early exit if not relevant)
// ============================================================

const labels = issue.labels.nodes.map(l => l.name);
const domainLabels = ${{ env.DOMAIN_LABELS }};
const hasDomainLabel = labels.some(label =>
domainLabels.some(domain => label.startsWith(domain))
);

if (!hasDomainLabel) {
console.log(`Issue #${issueNumber} has no domain label (${labels.join(', ') || 'none'}), skipping`);
return;
}

console.log(`Issue has domain label: ${labels.filter(l => l.startsWith('domain::')).join(', ')}`);

// ============================================================
// CHECK IF ALREADY IN PROJECT
// ============================================================

const existingItem = issue.projectItems.nodes.find(
item => item.project.id === project.id
);

if (existingItem) {
console.log('Issue is already in the project');
return;
}

// ============================================================
// ADD ISSUE TO PROJECT
// ============================================================

const addResult = await github.graphql(`
mutation($projectId: ID!, $contentId: ID!) {
addProjectV2ItemById(input: {
projectId: $projectId
contentId: $contentId
}) {
item {
id
}
}
}
`, {
projectId: project.id,
contentId: issue.id
});

const itemId = addResult.addProjectV2ItemById.item.id;
console.log(`Issue added to project (item: ${itemId})`);

// ============================================================
// SET STATUS TO TODO
// ============================================================

const statusField = project.fields.nodes.find(f => f.name === '${{ env.STATUS_FIELD_NAME }}');
const todoOption = statusField?.options.find(o => o.name === '${{ env.STATUS_TODO }}');

if (!statusField || !todoOption) {
console.log('Warning: Status field or Todo option not found');
return;
}

await github.graphql(`
mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $value: ProjectV2FieldValue!) {
updateProjectV2ItemFieldValue(input: {
projectId: $projectId
itemId: $itemId
fieldId: $fieldId
value: $value
}) {
projectV2Item { id }
}
}
`, {
projectId: project.id,
itemId,
fieldId: statusField.id,
value: { singleSelectOptionId: todoOption.id }
});

console.log(`Status → ${{ env.STATUS_TODO }}`);
console.log('Completed!');
Loading
Loading