diff --git a/.github/scripts/bot-next-issue-recommendation.js b/.github/scripts/bot-next-issue-recommendation.js index ae811f4f6..dba56fb5e 100644 --- a/.github/scripts/bot-next-issue-recommendation.js +++ b/.github/scripts/bot-next-issue-recommendation.js @@ -39,7 +39,7 @@ module.exports = async ({ github, context, core }) => { } // Get the first linked issue number - const issueNumber = parseInt(matches[0][2]); + const issueNumber = parseInt(matches[0][2], 10); core.info(`Found linked issue #${issueNumber}`); try { @@ -78,6 +78,7 @@ module.exports = async ({ github, context, core }) => { let recommendedIssues = []; let recommendedLabel = null; let isFallback = false; + let recommendationScope = 'repo'; recommendedIssues = await searchIssues(github, core, repoOwner, repoName, 'beginner'); recommendedLabel = 'Beginner'; @@ -88,6 +89,11 @@ module.exports = async ({ github, context, core }) => { recommendedLabel = 'Good First Issue'; } + if (recommendedIssues.length === 0) { + recommendationScope = 'org'; + recommendedLabel = 'Good First Issue'; + recommendedIssues = await searchOrgIssues(github, core, repoOwner, 'good first issue'); + } // Remove the issue they just solved recommendedIssues = recommendedIssues.filter(i => i.number !== issueNumber); @@ -99,9 +105,9 @@ module.exports = async ({ github, context, core }) => { completedLabelText, recommendedLabel, isFallback, + recommendationScope, }; await generateAndPostComment(github, context, core, prNumber, recommendedIssues, recommendationMeta); - } catch (error) { core.setFailed(`Error processing issue #${issueNumber}: ${error.message}`); } @@ -125,7 +131,32 @@ async function searchIssues(github, core, owner, repo, label) { } } -async function generateAndPostComment(github, context, core, prNumber, recommendedIssues, { completedLabelText, recommendedLabel, isFallback}) { +async function searchOrgIssues(github, core, owner, label) { + try { + const query = `org:${owner} type:issue state:open label:"${label}" no:assignee`; + core.info(`Searching org issues with query: ${query}`); + + const { data: searchResult } = await github.rest.search.issuesAndPullRequests({ + q: query, + per_page: 6, + }); + + core.info(`Found ${searchResult.items.length} org issues with label "${label}"`); + return searchResult.items; + } catch (error) { + core.warning(`Error searching org issues with label "${label}": ${error.message}`); + return []; + } +} + +async function generateAndPostComment( + github, + context, + core, + prNumber, + recommendedIssues, + { completedLabelText, recommendedLabel, isFallback, recommendationScope } +) { const marker = ''; // Build comment content @@ -133,8 +164,9 @@ async function generateAndPostComment(github, context, core, prNumber, recommend comment += `Thank you for your contribution to the Hiero Python SDK! We're excited to have you as part of our community.\n\n`; if (recommendedIssues.length > 0) { - - if (isFallback) { + if (recommendationScope === 'org') { + comment += 'Here are some **Good First Issues across the Hiero organization** you might be interested in working on next:\n\n'; + } else if (isFallback) { comment += `Here are some **${recommendedLabel}** issues at a similar level you might be interested in working on next:\n\n`; } else { comment += `Here are some issues labeled **${recommendedLabel}** you might be interested in working on next:\n\n`; @@ -159,37 +191,43 @@ async function generateAndPostComment(github, context, core, prNumber, recommend const description = sanitized.substring(0, 150); comment += ` ${description}${sanitized.length > 150 ? '...' : ''}\n\n`; } else { - comment += ` *No description available*\n\n`; + comment += ' *No description available*\n\n'; } }); } else { comment += `There are currently no open issues available at or near the ${completedLabelText} level in this repository.\n\n`; - comment += `You can check out **Good First Issues** in other Hiero repositories:\n\n`; - const repoQuery = SUPPORTED_GFI_REPOS - .map(repo => `repo:${context.repo.owner}/${repo}`) - .join(' OR '); - - const gfiSearchQuery = [ - 'is:open', - 'is:issue', - `org:${context.repo.owner}`, - 'archived:false', - 'no:assignee', - '(label:"good first issue" OR label:"skill: good first issue")', - `(${repoQuery})`, - ].join(' '); - - const gfiQuery = `https://github.com/issues?q=${encodeURIComponent(gfiSearchQuery)}`; - - comment += `[View Good First Issues across supported Hiero repositories](${gfiQuery})\n\n`; + if (recommendationScope === 'org') { + const orgLabel = recommendedLabel === 'Beginner' ? 'beginner' : 'good first issue'; + const orgQuery = `org:${context.repo.owner} type:issue state:open label:"${orgLabel}"`; + comment += `You can check out ${recommendedLabel.toLowerCase()} issues across the entire Hiero organization: ` + + `[Hiero ${recommendedLabel} Issues](https://github.com/issues?q=${encodeURIComponent(orgQuery)})\n\n`; + } else { + comment += 'You can check out **Good First Issues** in other Hiero repositories:\n\n'; + const repoQuery = SUPPORTED_GFI_REPOS + .map(repo => `repo:${context.repo.owner}/${repo}`) + .join(' OR '); + + const gfiSearchQuery = [ + 'is:open', + 'is:issue', + `org:${context.repo.owner}`, + 'archived:false', + 'no:assignee', + '(label:"good first issue" OR label:"skill: good first issue")', + `(${repoQuery})`, + ].join(' '); + + const gfiQuery = `https://github.com/issues?q=${encodeURIComponent(gfiSearchQuery)}`; + comment += `[View Good First Issues across supported Hiero repositories](${gfiQuery})\n\n`; + } } - comment += `🌟 **Stay connected with the project:**\n`; + comment += '🌟 **Stay connected with the project:**\n'; comment += `- ⭐ [Star this repository](https://github.com/${context.repo.owner}/${context.repo.repo}) to show your support\n`; comment += `- 👀 [Watch this repository](https://github.com/${context.repo.owner}/${context.repo.repo}/watchers) to get notified of new issues and releases\n\n`; - comment += `We look forward to seeing more contributions from you! If you have any questions, feel free to ask in our [Discord community](https://github.com/hiero-ledger/hiero-sdk-python/blob/main/docs/discord.md).\n\n`; - comment += `From the Hiero Python SDK Team 🚀`; + comment += 'We look forward to seeing more contributions from you! If you have any questions, feel free to ask in our [Discord community](https://github.com/hiero-ledger/hiero-sdk-python/blob/main/docs/discord.md).\n\n'; + comment += 'From the Hiero Python SDK Team 🚀'; // Check for existing comment try { diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b56c8dad..c13ed940f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. ## [Unreleased] ### Tests + - Standardize formatting of `tests/unit/entity_id_helper_test.py` using Black for consistent code style across the test suite (#1527) - Added tests for ProtoBuf Training Example Implementation @@ -28,13 +29,13 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. - Format tests/unit/network_tls_test.py with black for code style consistency (#1543) - Formatted `ethereum_transaction_test.py` using Black. - Formatted client_test.py using Black. -- Format tests/unit/query*.py using black (#1547) -- Format `tests/unit/custom_fee_test.py` with black for code style consistency. (#1525) ### Added + +- Add constructor-style `__repr__` for `FileId` to improve debugging output (#1628) - Added logging in bot-gfi-assign-on-comment.js to prevent silent skips. (`#1668`) - Added `AssessedCustomFee` domain model to represent assessed custom fees. (`#1637`) -- Add __repr__ method for ContractId class to improve debugging (#1714) +- Add `__repr__` method for ContractId class to improve debugging (#1714) - Added Protobuf Training guide to enhance developer understanding of proto serialization and deserialization (#1645) - Add `__repr__()` method to `TopicId` class for improved debugging with constructor-style representation (#1629) @@ -146,13 +147,15 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. - Added MirrorNode based population for `ContractId`, including parsing and serialization support. - Added `/working` command to reset the inactivity timer on issues and PRs. ([#1552](https://github.com/hiero-ledger/hiero-sdk-python/issues/1552)) - Added `grpc_deadline` support for transaction and query execution. -- Type hints to exception classes (`PrecheckError`, `MaxAttemptsError`, `ReceiptStatusError`) constructors and string methods. ### Documentation + +- Type hints to exception classes (`PrecheckError`, `MaxAttemptsError`, `ReceiptStatusError`) constructors and string methods. - Added comprehensive docstring to `compress_with_cryptography` function (#1626) - Replaced the docstring in `entity_id_helper.py` with one that is correct. (#1623) ### Changed + - Refactored `setup_client()` in all `examples/query/` files to use `Client.from_env()` for simplified client initialization (#1449) - Updated return of to_bytes function in `src/hiero_sdk_python/transaction/transaction.py`. (#1631) - Added missing return type `src/hiero_sdk_python/utils/entity_id_helper.py`. (#1622) @@ -253,6 +256,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. - chore: update MAINTAINERS.md to include new maintainer Manish Dait and sort maintainers by GitHub ID. (#1691) ### Fixed + - Updated Good First Issue recommendations to supported Hiero repositories. (#1689) - Fix the next-issue recommendation bot to post the correct issue recommendation comment. (#1593) - Ensured that the GFI assignment bot skips posting `/assign` reminders for repository collaborators to avoid unnecessary notifications.(#1568). @@ -417,6 +421,7 @@ This changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.1. - docs: Add `docs/sdk_developers/project_structure.md` to explain repository layout and import paths. ### Changed + - chore: renamed examples to match src where possible - Moved examples/ to be inside subfiles to match src structure - changed example script workflow to run on new subdirectory structure @@ -860,5 +865,4 @@ contract_call_local_pb2.ContractLoginfo -> contract_types_pb2.ContractLoginfo - N/A - # [0.1.0] - 2025-02-19 diff --git a/src/hiero_sdk_python/file/file_id.py b/src/hiero_sdk_python/file/file_id.py index bfc2e7271..c804258b2 100644 --- a/src/hiero_sdk_python/file/file_id.py +++ b/src/hiero_sdk_python/file/file_id.py @@ -106,6 +106,15 @@ def __str__(self) -> str: """ return f"{self.shard}.{self.realm}.{self.file}" + def __repr__(self) -> str: + """ + Returns a detailed representation of the FileId suitable for debugging. + + Returns: + str: A string in constructor format 'FileId(shard=X, realm=Y, file=Z)'. + """ + return f"FileId(shard={self.shard}, realm={self.realm}, file={self.file})" + def validate_checksum(self, client: Client) -> None: """ Validates the stored checksum against the calculated checksum using the provided client. diff --git a/tests/unit/file_id_test.py b/tests/unit/file_id_test.py index d8671e3d2..0e962e625 100644 --- a/tests/unit/file_id_test.py +++ b/tests/unit/file_id_test.py @@ -45,6 +45,20 @@ def test_str_representation_default(): assert str(file_id) == "0.0.0" +def test_repr_representation(): + """Test repr representation of FileId.""" + file_id = FileId(0, 0, 150) + + assert repr(file_id) == "FileId(shard=0, realm=0, file=150)" + + +def test_repr_representation_with_checksum(): + """Test repr representation of FileId with checksum.""" + file_id = FileId.from_string("0.0.1-dfkxr") + + assert repr(file_id) == "FileId(shard=0, realm=0, file=1)" + + def test_from_string_valid(): """Test creating FileId from valid string format.""" file_id = FileId.from_string("1.2.3")