Skip to content

leebaroneau/notion-github-sync

Repository files navigation

Notion GitHub Sync

Standalone worker that keeps a Notion intake/reporting database, GitHub Issues, and GitHub Projects in tune, with GitHub as the source of truth.

The current flow is:

Notion Form response -> GitHub Issue -> GitHub Project item -> Notion mirror fields

The production GitHub Project for this worker is:

Authority Model

GitHub owns operational state:

  • issue state
  • assignee
  • labels
  • milestone
  • project status
  • project priority
  • roadmap / track
  • request type
  • target date
  • owner
  • source

Notion can initiate only narrow GitHub-shaped commands:

  • create the initial issue from the form response
  • post a requester follow-up as a GitHub issue comment
  • request a configured priority label such as priority: high

The worker overwrites Notion mirror fields from GitHub on each pass. Do not use Notion-only status fields for engineering workflow state. If a GitHub Project value changes, the next poll writes that value back to Notion.

Required Notion Properties

Create a Notion Form backed by a database with these properties. The exact names matter.

Property Purpose
Title GitHub issue title
Request Type Maps to type: <value> label
Description GitHub issue ## Request section
Business Context GitHub issue ## Business Context section
Acceptance Criteria GitHub issue ## Acceptance Criteria section
Requester Name GitHub issue requester metadata
Requester Email GitHub issue requester metadata when NOTION_GITHUB_SYNC_INCLUDE_REQUESTER_EMAIL=1
Company GitHub issue requester metadata
Business Priority Must exactly match one allowed GitHub priority label
Requester Follow-up Text to post as a GitHub issue comment
Send Follow-up Checkbox command that posts Requester Follow-up once

Add these mirror and sync fields for the worker to write back:

GitHub Issue URL
GitHub Issue Number
GitHub Issue Node ID
GitHub State
GitHub State Reason
GitHub Labels
GitHub Assignee
GitHub Project Status
GitHub Project Priority
GitHub Project Roadmap
GitHub Project Request Type
GitHub Project Target Date
GitHub Project Owner
GitHub Project Source
GitHub Project Item ID
GitHub Milestone
GitHub Closed At
Last GitHub Sync
Last GitHub Project Sync
Sync Status
Sync Error
Last Sync Direction
Last Sync Actor
Last Notion Hash
Last GitHub Hash
Last Comment Hash
Last Processed At

Use Notion property types that match the field intent: number for issue number, URL for issue URL, checkbox for Send Follow-up, date for sync timestamps and target dates, multi-select for labels if you want labels split into options, and rich text for the rest.

GitHub Setup

Create the labels referenced by your form and env vars before enabling sync:

intake
from:notion
priority: low
priority: medium
priority: high
type: feature-request
type: bug-report
type: ops
type: content
type: support
type: other
needs:verification

GitHub Projects should be the canonical shape for the workflow. At minimum, configure these Project fields:

Field Recommended type Purpose
Status Single select Workflow state: Inbox, Ready, In Progress, Blocked, Done, Wont Do
Priority Single select Low, Medium, High
Roadmap Single select Now, Next, Later
Request Type Single select Feature Request, Bug Report, Ops, Content, Support, Other
Target Date Date Planned delivery or review date
Assignees Built-in people field GitHub owner for the work; mirrored into Notion's GitHub Project Owner
Source Single select Notion, GitHub, Internal, Customer, Sales, Support

The worker adds linked issues to the Project using GITHUB_PROJECT_ID. Newly-created Notion intake issues are initialized as:

Status = Inbox
Source = Notion
Request Type = <Notion Request Type>
Priority = <Notion Business Priority, converted from priority: high -> High>

Triage then happens in GitHub Projects. Notion reports what GitHub says.

Issue To Resolution Workflow

  1. Intake lands in Inbox from Notion or a GitHub issue template.
  2. Triage sets Request Type, Priority, Roadmap, Target Date, and Assignees.
  3. Accepted work moves to Ready.
  4. Active work moves to In Progress; blocked work moves to Blocked.
  5. Work only moves to Done after the issue's acceptance criteria have been verified.
  6. Work that will not be done moves to Wont Do with a closing comment.

Repo guardrails:

  • GitHub issue forms are enabled for feature, bug, ops, content, and support requests.
  • CI runs npm test and a Docker build on pushes to main and on pull requests.
  • Pull requests include a verification checklist and require the Project status to reflect the real state.

Configuration

One running worker targets one Notion database and one GitHub owner/repo.

NOTION_GITHUB_SYNC_ENABLED=1
NOTION_TOKEN=<notion integration token>
NOTION_INTAKE_DATABASE_ID=363333cc46cc8019b6d9cfed97ccd479
GITHUB_TOKEN=<fine-grained token with issues:write>
GITHUB_OWNER=leebaroneau
GITHUB_REPO=notion-github-sync
GITHUB_PROJECT_ID=PVT_kwHOAfzT1c4BX_7o
GITHUB_PROJECT_FIELD_STATUS=Status
GITHUB_PROJECT_FIELD_PRIORITY=Priority
GITHUB_PROJECT_FIELD_ROADMAP=Roadmap
GITHUB_PROJECT_FIELD_REQUEST_TYPE=Request Type
GITHUB_PROJECT_FIELD_TARGET_DATE=Target Date
GITHUB_PROJECT_FIELD_OWNER=Assignees
GITHUB_PROJECT_FIELD_SOURCE=Source
GITHUB_PROJECT_DEFAULT_STATUS=Inbox
GITHUB_PROJECT_DEFAULT_SOURCE=Notion
NOTION_GITHUB_SYNC_LABELS=intake,from:notion
NOTION_GITHUB_SYNC_ALLOWED_PRIORITY_LABELS=priority: low,priority: medium,priority: high
NOTION_GITHUB_SYNC_INCLUDE_REQUESTER_EMAIL=1

GITHUB_PROJECT_ID is the Project v2 node ID, not the project number. You can find it with GitHub's GraphQL API or gh api graphql. The token needs issue write access for the repository and GitHub Projects access. For a classic token, use repo, read:org, and project; for a read-only Projects mirror, read:project is enough for queries, but this worker adds items to the Project, so it needs project mutation access.

To make it specific to a GitHub organization, set GITHUB_OWNER to the org, set GITHUB_REPO to that org's canonical intake repo, and use org-specific labels or a dedicated Notion database. For multiple orgs or separate canonical boards, run separate workers with separate env vars.

Notion Setup

Create a Notion internal integration, copy its integration token into NOTION_TOKEN, and share the intake database with that integration. V1 does not require Notion webhooks because the worker polls and reconciles drift from GitHub back into Notion.

The Notion database needs two groups of fields:

  • Intake command fields: fields that non-GitHub users fill in, such as title, request type, description, business context, acceptance criteria, requester, and follow-up.
  • Mirror fields: fields this worker overwrites from GitHub Issues and GitHub Projects. Treat these as read-only in Notion.

If Notion property names differ from the names listed above, rename the Notion properties for V1. Configurable property-name mapping can be added later if needed, but exact names keep the first production version simpler and less drift-prone.

Deploying With Coolify

Coolify can deploy this directly from the Git repository. Use either:

  • Dockerfile build pack: recommended for predictable worker runtime. Coolify builds the included Dockerfile from this repo.
  • Nixpacks: possible because this is a plain Node app with npm start, but less explicit than the Dockerfile path.

This worker does not need a public domain. Configure the env vars in Coolify and deploy it as a background service.

Local Use

cp .env.example .env
npm test
npm start

About

No description or website provided.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors