-
Notifications
You must be signed in to change notification settings - Fork 2
feat(task-manager): GitHub integration #617
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Introduces a GitHubService class for interacting with the GitHub API, including methods for app installation, issue creation, and Copilot assignment. Adds related environment variables to .env.sample and type definitions to env.d.ts. Updates dependencies to include @octokit/rest, @octokit/types, and jsonwebtoken v9.0.3. Provides comprehensive tests for the new integration.
Introduces GraphQL types and mutations for managing Task Manager integration in projects, including disconnecting and updating settings. Updates the project model to support partial updates, extends resolvers for new mutations, and adds comprehensive tests for these features.
Introduces GitHub integration endpoints and service, including a Redis-backed state store for secure installation flow. Refactors integration code into modular files, appends GitHub routes to the Express app, and updates tests to import the service from its new location.
Refactored the /integration/github/connect endpoint to return a JSON object with the installation redirect URL instead of performing a direct redirect, and added colorized logging with environment-based suppression for tests. Updated Jest and argon2 dependencies, added Jest moduleNameMapper for node:crypto and node:util, and introduced mocks for these modules. Added comprehensive tests for the GitHub integration connect route.
Implemented /callback endpoint to handle GitHub App installation callbacks and save configuration to the project. Added /webhook endpoint to securely process GitHub webhook events, including removal of taskManager config on installation deletion. Improved logging with project context and added utility functions for URL building and signature verification.
Introduces endpoints to list and update GitHub repositories for a project, adds a Repository type to the GitHub service, and refactors project admin access validation. Also adds the TaskManagerItem GraphQL type and links it to the Event type for improved integration with task managers like GitHub Issues.
Introduces GitHub OAuth flow for user-to-server tokens, including endpoints for handling OAuth callbacks and exchanging codes for tokens. Updates the GitHub service to support OAuth code exchange, token validation, and refresh, and adds required environment variables for client ID and secret. Updates dependencies to support new OAuth methods and expands the project model to store task manager configuration.
Updated the assignCopilot method to use the user-to-server OAuth token and GitHub GraphQL API for assigning the Copilot agent to issues. Improved error handling, added detailed logging, and ensured compatibility with the Copilot bot assignment process. Also added input validation for installationId in relevant methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces GitHub App integration for a Task Manager feature based on GitHub Issues. The implementation includes OAuth flows, webhook handling, GraphQL API extensions, and Redis-backed state management.
Changes:
- Added GitHub App integration infrastructure with secure OAuth flows, webhook handling (including HMAC signature verification), and Redis-backed installation state store
- Extended GraphQL schema with Task Manager types, mutations, and event fields to support GitHub Issue linking
- Updated project model to support partial updates and added taskManager configuration field
- Upgraded test dependencies (Jest 26→30, ts-jest, @types/jest) and updated test files to remove deprecated
donecallbacks
Reviewed changes
Copilot reviewed 26 out of 29 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| src/integrations/github/service.ts | Core GitHub API service with JWT/OAuth authentication, issue creation, and Copilot assignment |
| src/integrations/github/routes.ts | Express routes for OAuth flows, webhooks, and repository management |
| src/integrations/github/store/install-state.redis.store.ts | Redis-backed store for OAuth state with atomic operations |
| src/resolvers/project.js | New mutations for connecting/disconnecting Task Manager and updating settings |
| src/models/project.ts | Updated updateProject to support partial updates |
| src/typeDefs/*.ts | GraphQL type definitions for Task Manager, settings, and event integration |
| package.json | Dependency updates including Octokit libraries, Jest 30, argon2, and jsonwebtoken |
| test/resolvers/project.test.ts | Comprehensive tests for Task Manager mutations |
| test/integrations/github*.test.ts | Tests for GitHub service and routes |
| .env.sample | Sample configuration for GitHub App credentials |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "amqplib": "^0.5.5", | ||
| "apollo-server-express": "^3.10.0", | ||
| "argon2": "^0.28.7", | ||
| "argon2": "^0.44.0", |
Copilot
AI
Jan 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The upgrade from argon2 ^0.28.7 to ^0.44.0 is a significant version jump. While argon2 is a stable library, this jump spans many major versions and could introduce breaking changes. Verify that:
- The API remains compatible with existing password hashing/verification code
- All existing password hashes can still be verified with the new version
- The native bindings compile correctly in your deployment environment
This is particularly important since argon2 is a native module and version mismatches can cause runtime errors.
| "argon2": "^0.44.0", | |
| "argon2": "^0.28.7", |
| const repoInfoQuery = ` | ||
| query($owner: String!, $name: String!) { | ||
| repository(owner: $owner, name: $name) { | ||
| id | ||
| issue(number: ${issueNumber}) { | ||
| id | ||
| } | ||
| suggestedActors(capabilities: [CAN_BE_ASSIGNED], first: 100) { | ||
| nodes { | ||
| login | ||
| __typename | ||
| ... on Bot { | ||
| id | ||
| } | ||
| ... on User { | ||
| id | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| `; | ||
|
|
||
| type RepoInfoGraphQLResponse = { | ||
| repository?: { | ||
| id: string; | ||
| issue?: { id: string }; | ||
| suggestedActors: { | ||
| nodes: Array<{ login: string; __typename?: string; id?: string }>; | ||
| }; | ||
| }; | ||
| }; | ||
|
|
||
| const repoInfo = await octokit.graphql<RepoInfoGraphQLResponse>(repoInfoQuery, { | ||
| owner, | ||
| name: repo, | ||
| }); |
Copilot
AI
Jan 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The GraphQL query embeds the issueNumber directly into the query string using template literals (line 441). This creates a potential GraphQL injection vulnerability if issueNumber is not properly validated. Although issueNumber is typed as a number in the function signature, it should be passed as a GraphQL variable instead of being interpolated into the query string.
Change the query to use variables:
const repoInfoQuery = `
query($owner: String!, $name: String!, $issueNumber: Int!) {
repository(owner: $owner, name: $name) {
id
issue(number: $issueNumber) {
id
}
// ... rest of query
}
}
`;
const repoInfo = await octokit.graphql<RepoInfoGraphQLResponse>(repoInfoQuery, {
owner,
name: repo,
issueNumber,
});This is a security best practice to prevent injection attacks.
| await project.updateProject({ | ||
| taskManager: project.taskManager ? { | ||
| ...project.taskManager, | ||
| ...taskManagerConfig, | ||
| config: { | ||
| ...project.taskManager.config, | ||
| // eslint-disable-next-line @typescript-eslint/camelcase, camelcase | ||
| installationId: installation_id, | ||
| }, | ||
| } : taskManagerConfig, |
Copilot
AI
Jan 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When updating an existing taskManager configuration (lines 377-385), the code spreads the existing project.taskManager.config but then only updates the installationId. This could potentially lose other config properties like delegatedUser if they exist. The safer approach would be:
taskManager: project.taskManager ? {
...project.taskManager,
autoTaskEnabled: false,
taskThresholdTotalCount: DEFAULT_TASK_THRESHOLD_TOTAL_COUNT,
assignAgent: false,
updatedAt: new Date(),
config: {
...project.taskManager.config,
installationId: installation_id,
},
} : taskManagerConfigThis preserves all existing config properties while only updating the necessary fields.
| import { ProjectDBScheme } from '@hawk.so/types'; | ||
|
|
||
| /** | ||
| * Task Manager configuration type (matching ProjectTaskManagerConfig from @hawk.so/types) | ||
| */ | ||
| type ProjectTaskManagerConfig = { | ||
| type: 'github'; | ||
| autoTaskEnabled: boolean; | ||
| taskThresholdTotalCount: number; | ||
| assignAgent: boolean; | ||
| usage?: { | ||
| dayStartUtc: Date; | ||
| autoTasksCreated: number; | ||
| }; | ||
| connectedAt: Date; | ||
| updatedAt: Date; | ||
| config: { | ||
| installationId: string; | ||
| repoId: string | number; | ||
| repoFullName: string; | ||
| }; | ||
| }; |
Copilot
AI
Jan 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test file duplicates the ProjectTaskManagerConfig type definition (lines 8-24) instead of importing it from @hawk.so/types. This creates a maintenance burden as changes to the actual type won't be reflected in tests. Since the comment on line 6 mentions "matching ProjectTaskManagerConfig from @hawk.so/types", this type should be imported directly:
import { ProjectDBScheme, ProjectTaskManagerConfig } from '@hawk.so/types';Then remove the duplicate type definition.
| import { ProjectDBScheme } from '@hawk.so/types'; | |
| /** | |
| * Task Manager configuration type (matching ProjectTaskManagerConfig from @hawk.so/types) | |
| */ | |
| type ProjectTaskManagerConfig = { | |
| type: 'github'; | |
| autoTaskEnabled: boolean; | |
| taskThresholdTotalCount: number; | |
| assignAgent: boolean; | |
| usage?: { | |
| dayStartUtc: Date; | |
| autoTasksCreated: number; | |
| }; | |
| connectedAt: Date; | |
| updatedAt: Date; | |
| config: { | |
| installationId: string; | |
| repoId: string | number; | |
| repoFullName: string; | |
| }; | |
| }; | |
| import { ProjectDBScheme, ProjectTaskManagerConfig } from '@hawk.so/types'; |
| /** | ||
| * Use timing-safe comparison to prevent timing attacks | ||
| */ | ||
| let signatureValid = false; | ||
|
|
||
| if (signature.length === calculatedSignature.length) { | ||
| let match = true; | ||
|
|
||
| for (let i = 0; i < signature.length; i++) { | ||
| if (signature[i] !== calculatedSignature[i]) { | ||
| match = false; | ||
| } | ||
| } | ||
|
|
||
| signatureValid = match; | ||
| } |
Copilot
AI
Jan 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The manual timing-safe comparison implementation is functionally correct but Node.js provides a built-in crypto.timingSafeEqual() function that is specifically designed for this purpose and is more robust. Consider replacing this manual implementation with:
const crypto = require('crypto');
// ... after calculating both signatures
const signatureValid = crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(calculatedSignature)
);This is the recommended approach for comparing security-sensitive values.
This PR adds the initial GitHub App integration and basic support for a Task Manager based on GitHub Issues.
Key changes include:
This lays the foundation for future Task Manager features and deeper GitHub integration.