Skip to content

Conversation

@jensenbox
Copy link
Member

@jensenbox jensenbox commented Jun 9, 2025

User description

  • Integrated OpenAIGenerator for generating AI-powered release notes.
  • Added Notion support to document release notes with generated content.

Description

  • Integrated AI-powered release notes generation using the new OpenAIGenerator class, leveraging the OpenAI API.

  • Added automated documentation of release notes in Notion via the new DocumentationNotion class, including detailed issue metadata.

  • Expanded Linear GraphQL client models, enums, and queries to support richer issue, project, customer, and notification data.

  • Updated deploy flow to generate AI release notes, document them in Notion, and include the Notion URL in Slack deploy messages.

  • Added new configuration settings for Notion and OpenAI integration.

  • Updated dependencies to include notion-client and openai.

  • Improved Slack messaging to link directly to Notion release notes.

  • Marked the documentation directory as a Python package.


Changes walkthrough 📝

Relevant files
Enhancement
11 files
input_types.py
Major expansion of Linear GraphQL input types and models for broader
API coverage

src/release_helper/issue_management/linear/graphql_client/input_types.py

  • Added many new Pydantic models and fields to support expanded Linear
    GraphQL API types, especially for customers, initiatives, projects,
    and issues.
  • Enhanced existing models with new optional fields, filters, and sort
    options (e.g., for customers, issues, projects).
  • Introduced new models for integrations, notifications, and advanced
    filtering/sorting.
  • Updated and extended enums and input types to match new Linear API
    capabilities.
  • +923/-230
    __init__.py
    Update Linear GraphQL client exports for new API types and models

    src/release_helper/issue_management/linear/graphql_client/init.py

  • Added imports and __all__ entries for many new types, enums, and input
    models reflecting the expanded Linear GraphQL API.
  • Removed deprecated or obsolete imports.
  • Synchronized with the new/updated models in input_types.py for broader
    API support.
  • +138/-44
    main.py
    Integrate AI release notes generation and Notion documentation in
    deploy flow

    src/release_helper/main.py

  • Integrated OpenAIGenerator to generate AI-powered release notes after
    deployment.
  • Added DocumentationNotion to create and document release notes in
    Notion with generated content.
  • Updated deploy flow to include AI content generation and Notion
    documentation, and to pass Notion URL to Slack deploy message.
  • Improved logging to reflect new documentation steps.
  • +29/-1   
    issue.py
    Enhance Issue models with labels, project, and additional fields

    src/release_helper/issue_management/linear/graphql_client/issue.py

  • Extended IssueIssue model to include description, priority,
    priority_label, created_at, completed_at, labels, and project.
  • Added new models: IssueIssueLabels, IssueIssueLabelsNodes, and
    IssueIssueProject for richer issue data.
  • Updated IssueIssueTeam to include name.
  • +27/-1   
    slack.py
    Add Notion release notes link to Slack deploy message       

    src/release_helper/messaging/slack.py

  • Modified send_deploy_message to accept and display a Notion URL for
    release notes.
  • Updated Slack message formatting to include a link to Notion release
    notes.
  • +7/-2     
    client.py
    Expand issue GraphQL query to fetch labels, project, and more fields

    src/release_helper/issue_management/linear/graphql_client/client.py

  • Updated the issue GraphQL query to fetch additional fields:
    description, priority, priorityLabel, createdAt, completedAt, labels,
    and project.
  • Synchronized the client-side query with the expanded Issue models.
  • +19/-0   
    settings.py
    Add Notion and OpenAI settings for documentation and AI integration

    src/release_helper/settings.py

  • Added NotionSettings and OpenAISettings classes for new documentation
    and AI features.
  • Updated HelperSettings to include notion and openai configuration
    sections.
  • +12/-0   
    queries.graphql
    Expand Issue GraphQL query to include labels and project details

    src/release_helper/issue_management/linear/queries.graphql

  • Expanded the Issue GraphQL query to include description, priority,
    priorityLabel, createdAt, completedAt, labels, and project (with
    initiatives).
  • Enhanced the query to support richer issue and project data retrieval.
  • +26/-0   
    openai_generator.py
    Add OpenAIGenerator class for AI-powered release notes generation

    src/release_helper/documentation/openai_generator.py

  • Introduces a new OpenAIGenerator class for generating AI-powered
    release notes.
  • Implements logic to format issue data and create prompts for OpenAI.
  • Integrates OpenAI API for generating human-readable summaries of
    releases.
  • Handles extraction and formatting of release and issue information for
    prompt construction.
  • +208/-0 
    notion.py
    Add DocumentationNotion class for automated Notion release notes

    src/release_helper/documentation/notion.py

  • Adds a new DocumentationNotion class for creating release notes pages
    in Notion.
  • Implements logic to format release and issue data into Notion block
    structures.
  • Supports detailed inclusion of issue metadata (team, assignee,
    priority, labels, project, description).
  • Integrates with Notion API to create structured release notes pages.
  • +270/-0 
    enums.py
    Expand and update Linear GraphQL enums for broader schema support

    src/release_helper/issue_management/linear/graphql_client/enums.py

  • Adds multiple new enum classes and values to support expanded Linear
    GraphQL schema.
  • Introduces enums for notification types, post types, pull request
    statuses, semantic search, and more.
  • Updates and extends existing enums with new values (e.g.,
    IntegrationService, UserRoleType, ViewType).
  • Removes some workflow-related enums and adds new ones for notification
    and project management.
  • +229/-29
    Miscellaneous
    1 files
    __init__.py
    Make documentation directory a Python package                       

    src/release_helper/documentation/init.py

  • Added an empty __init__.py to mark the documentation directory as a
    Python package.
  • +1/-0     
    Dependencies
    1 files
    pyproject.toml
    Add dependencies for Notion and OpenAI integration             

    pyproject.toml

  • Adds notion-client and openai as new dependencies.
  • Updates httpx version requirement.
  • +3/-1     
    Additional files
    1 files
    schema.graphql [link]   

    Need help?
  • Type /help how to ... in the comments thread for any questions about Qodo Merge usage.
  • Check out the documentation for more information.
  • - Integrated OpenAIGenerator for generating AI-powered release notes.
    - Added Notion support to document release notes with generated content.
    @qodo-code-review qodo-code-review bot changed the title DEVX-35 Add automated generation and documentation of release notes DEVX-35 feat: Add automated AI-powered release notes generation and Notion documentation Jun 9, 2025
    @qodo-code-review
    Copy link

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
    🧪 No relevant tests
    🔒 Security concerns

    Sensitive information exposure:
    The OpenAI API key and Notion token are stored in settings and passed around as plain strings. While this follows common patterns, ensure these are properly secured in the deployment environment and not logged. The OpenAI client initialization and Notion client creation should validate that these credentials are not empty or default values to prevent accidental exposure of placeholder credentials.

    ⚡ Recommended focus areas for review

    Error Handling

    The AI content generation and Notion page creation are not wrapped in try-catch blocks. If either OpenAI or Notion APIs fail, the entire deployment process could crash, potentially leaving the system in an inconsistent state where GitHub deployment succeeds but documentation fails.

    openai_generator = OpenAIGenerator(
        api_key=settings.helper.openai.api_key, model=settings.helper.openai.model
    )
    ai_content = openai_generator.generate_release_notes(
        release_title=release_draft.title,
        release_body=release_draft.body,
        issues=issues,
    )
    logger.info("Successfully generated AI release notes")
    
    notion = DocumentationNotion(
        token=settings.helper.notion.token,
        parent_page_id=settings.helper.notion.parent_page_id,
    )
    notion_url = notion.create_release_notes_page(
        release_title=release_draft.title,
        release_body=release_draft.body,
        release_url=f"https://github.com/{settings.github_repository}/releases/tag/{release_draft.tag_name}",
        issues=issues,
        ai_generated_content=ai_content,
    )
    logger.info(f"Created Notion page for release notes: {notion_url}")
    Configuration Issue

    The notion and openai settings are set to None as defaults, but the main deployment flow assumes they exist and will cause AttributeError when accessing their properties. These should either be required fields or the code should handle their absence gracefully.

    notion: NotionSettings = None
    openai: OpenAISettings = None
    Magic Number

    The hardcoded value 200 for description truncation and 197 for the truncation point lacks explanation and could be made configurable. This affects the quality of AI-generated content when issue descriptions are long.

    if len(description) > 200:  # noqa: PLR2004
        description = description[:197] + "..."

    @qodo-code-review
    Copy link

    qodo-code-review bot commented Jun 9, 2025

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Impact
    General
    Add customer identifier validation

    The CustomerNeedCreateInput model has both customer_id and customer_external_id
    as optional fields, but typically one should be required to identify the
    customer. Consider adding validation to ensure at least one customer identifier
    is provided.

    src/release_helper/issue_management/linear/graphql_client/input_types.py [384-398]

     class CustomerNeedCreateInput(BaseModel):
         attachment_id: Optional[str] = Field(alias="attachmentId", default=None)
         attachment_url: Optional[str] = Field(alias="attachmentUrl", default=None)
         body: Optional[str] = None
         body_data: Optional[Any] = Field(alias="bodyData", default=None)
         comment_id: Optional[str] = Field(alias="commentId", default=None)
         create_as_user: Optional[str] = Field(alias="createAsUser", default=None)
         customer_external_id: Optional[str] = Field(
             alias="customerExternalId", default=None
         )
         customer_id: Optional[str] = Field(alias="customerId", default=None)
         display_icon_url: Optional[str] = Field(alias="displayIconUrl", default=None)
         id: Optional[str] = None
         issue_id: Optional[str] = Field(alias="issueId", default=None)
         priority: Optional[float] = None
     
    +    @validator('customer_id')
    +    def validate_customer_identifier(cls, v, values):
    +        if not v and not values.get('customer_external_id'):
    +            raise ValueError('Either customer_id or customer_external_id must be provided')
    +        return v
    +
    • Apply / Chat
    Suggestion importance[1-10]: 7

    __

    Why: The suggestion correctly identifies that the PR makes both customer_id and customer_external_id optional, which could lead to invalid API calls. Adding a validator to ensure at least one is provided improves the model's robustness and provides better error handling.

    Medium
    Add SLA field consistency validation

    The IssueCreateInput model has inconsistent SLA field validation. If
    sla_started_at is provided, sla_type should typically be required to define the
    SLA calculation method.

    src/release_helper/issue_management/linear/graphql_client/input_types.py [1368-1414]

     class IssueCreateInput(BaseModel):
         assignee_id: Optional[str] = Field(alias="assigneeId", default=None)
         completed_at: Optional[Any] = Field(alias="completedAt", default=None)
         create_as_user: Optional[str] = Field(alias="createAsUser", default=None)
         created_at: Optional[Any] = Field(alias="createdAt", default=None)
         cycle_id: Optional[str] = Field(alias="cycleId", default=None)
         description: Optional[str] = None
         description_data: Optional[Any] = Field(alias="descriptionData", default=None)
         display_icon_url: Optional[str] = Field(alias="displayIconUrl", default=None)
         due_date: Optional[Any] = Field(alias="dueDate", default=None)
         estimate: Optional[float] = None
         external_user_creator: Optional[str] = Field(
             alias="externalUserCreator", default=None
         )
         id: Optional[str] = None
         label_ids: Optional[List[str]] = Field(alias="labelIds", default=None)
         last_applied_template_id: Optional[str] = Field(
             alias="lastAppliedTemplateId", default=None
         )
         parent_id: Optional[str] = Field(alias="parentId", default=None)
         priority: Optional[float] = None
         project_id: Optional[str] = Field(alias="projectId", default=None)
         project_milestone_id: Optional[str] = Field(
             alias="projectMilestoneId", default=None
         )
         reference_comment_id: Optional[str] = Field(
             alias="referenceCommentId", default=None
         )
         sla_breaches_at: Optional[Any] = Field(alias="slaBreachesAt", default=None)
         sla_started_at: Optional[Any] = Field(alias="slaStartedAt", default=None)
         sla_type: Optional[SLADayCountType] = Field(alias="slaType", default=None)
         sort_order: Optional[float] = Field(alias="sortOrder", default=None)
         source_comment_id: Optional[str] = Field(alias="sourceCommentId", default=None)
         source_pull_request_comment_id: Optional[str] = Field(
             alias="sourcePullRequestCommentId", default=None
         )
         state_id: Optional[str] = Field(alias="stateId", default=None)
         sub_issue_sort_order: Optional[float] = Field(
             alias="subIssueSortOrder", default=None
         )
         subscriber_ids: Optional[List[str]] = Field(alias="subscriberIds", default=None)
         team_id: str = Field(alias="teamId")
         title: str
         triaged_at: Optional[Any] = Field(alias="triagedAt", default=None)
     
    +    @validator('sla_type')
    +    def validate_sla_fields(cls, v, values):
    +        if values.get('sla_started_at') and not v:
    +            raise ValueError('sla_type is required when sla_started_at is provided')
    +        return v
    +

    [To ensure code accuracy, apply this suggestion manually]

    Suggestion importance[1-10]: 7

    __

    Why: The suggestion correctly points out a potential inconsistency between the new SLA-related fields. Making sla_type mandatory when sla_started_at is set is a logical validation that improves the data integrity of the input model and prevents potentially invalid states.

    Medium
    Extract magic numbers to constants
    Suggestion Impact:The commit directly implements the suggestion by extracting magic numbers to constants (DESCRIPTION_MAX_LENGTH = 200, TRUNCATION_SUFFIX = "...") and fixing the truncation logic to properly account for the suffix length

    code diff:

    +                DESCRIPTION_MAX_LENGTH = 200
    +                TRUNCATION_SUFFIX = "..."
    +
    +                if len(description) > DESCRIPTION_MAX_LENGTH:
    +                    description = description[:DESCRIPTION_MAX_LENGTH - len(TRUNCATION_SUFFIX)] + TRUNCATION_SUFFIX

    The magic number 200 should be extracted to a class constant for better
    maintainability. Also, the slice index 197 creates an inconsistency with the
    check condition.

    src/release_helper/documentation/openai_generator.py [199-200]

    -if len(description) > 200:  # noqa: PLR2004
    -    description = description[:197] + "..."
    +DESCRIPTION_MAX_LENGTH = 200
    +TRUNCATION_SUFFIX = "..."
     
    +if len(description) > DESCRIPTION_MAX_LENGTH:
    +    description = description[:DESCRIPTION_MAX_LENGTH - len(TRUNCATION_SUFFIX)] + TRUNCATION_SUFFIX
    +

    [Suggestion processed]

    Suggestion importance[1-10]: 7

    __

    Why: This is a valuable suggestion that improves code quality by removing magic numbers (200, 197) and replacing them with named constants. It also corrects a subtle logic issue in the string truncation, making the code more robust and maintainable.

    Medium
    Handle nullable nested fields properly

    Add error handling for nested fields that might be null. The assignee, project,
    and initiatives fields can be null, which could cause issues when processing the
    response. Consider making these fields optional in your data models or add null
    checks.

    src/release_helper/issue_management/linear/queries.graphql [1-51]

    +query Issue($issueId: String!) {
    +    issue(id: $issueId) {
    +        id
    +        title
    +        description
    +        priority
    +        priorityLabel
    +        createdAt
    +        completedAt
    +        state {
    +            name
    +            type
    +        }
    +        assignee {
    +            id
    +            name
    +            email
    +            displayName
    +        }
    +        creator {
    +            id
    +            name
    +            email
    +            displayName
    +        }
    +        identifier
    +        team {
    +            key
    +            name
    +        }
    +        labels {
    +            nodes {
    +                id
    +                name
    +                color
    +            }
    +        }
    +        project {
    +            id
    +            name
    +            description
    +            url
    +            initiatives {
    +                nodes {
    +                    id
    +                    name
    +                    description
    +                }
    +            }
    +        }
    +    }
    +}
     
    -

    [To ensure code accuracy, apply this suggestion manually]

    Suggestion importance[1-10]: 7

    __

    Why: The suggestion correctly points out that fields like assignee and project in the GraphQL query may be nullable. Failing to handle these potential null values in the client code that processes the query's response could lead to runtime errors. The score is capped at 7 as the improved_code is identical to the existing_code, and the suggestion is a valuable reminder for robust implementation.

    Medium
    Improve attribute access safety
    Suggestion Impact:The suggestion was directly implemented - the code was changed from using hasattr check to getattr with fallback, exactly as suggested

    code diff:

    -            team_name = (
    -                issue.team.name if hasattr(issue.team, "name") else issue.team.key
    -            )
    +            team_name = getattr(issue.team, "name", None) or issue.team.key

    Use a more robust attribute access pattern to avoid potential AttributeError.
    The current hasattr check may not be sufficient if the attribute exists but is
    None or empty.

    src/release_helper/documentation/notion.py [135-138]

     # Add team info
    -team_name = (
    -    issue.team.name if hasattr(issue.team, "name") else issue.team.key
    -)
    +team_name = getattr(issue.team, "name", None) or issue.team.key

    [Suggestion processed]

    Suggestion importance[1-10]: 5

    __

    Why: The suggestion proposes using getattr which is a more robust and Pythonic way to access an attribute that may not exist. While the original hasattr check works, the suggested change is cleaner and improves code readability.

    Low
    • Update

    Co-authored-by: qodo-merge-pro[bot] <151058649+qodo-merge-pro[bot]@users.noreply.github.com>
    jensenbox and others added 2 commits June 9, 2025 13:22
    Co-authored-by: qodo-merge-pro[bot] <151058649+qodo-merge-pro[bot]@users.noreply.github.com>
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Development

    Successfully merging this pull request may close these issues.

    2 participants