-
-
Notifications
You must be signed in to change notification settings - Fork 11.6k
v2.7.4: 新增下载进度条,可美观展示下载进度;新增全站评论方法;下载返回值优化,新增清单与耗时统计。完善发版流程和文档 #560
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
Open
hect0x7
wants to merge
19
commits into
master
Choose a base branch
from
dev
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
f6a0a24
test: define download manifest contract
hect0x7 94ddaa0
feat: add download manifest results
hect0x7 5d3c740
test: align cache assertions with copy semantics
hect0x7 9991cb9
test: consolidate download manifest coverage
hect0x7 24792ad
feat: add download manifest and release flow
hect0x7 59f9550
test: align feature tests with task context
hect0x7 c85c775
docs: reorganize tutorials and restore changelog
hect0x7 c0206cf
test: isolate batch failure log assertion
hect0x7 48bcedf
fix: address v2.7.4 review defects
hect0x7 f00f3f5
fix: remove invalid auto release dispatch
hect0x7 f7fea6d
fix: enforce manifest lifecycle contracts
hect0x7 4e6c249
refactor: simplify cached image download flow
hect0x7 5a9cf86
feat: add download progress and API improvements
hect0x7 a54a3f3
docs: improve GitHub Actions tutorial image
hect0x7 bb7dd2a
feat: enable CLI download progress by default
hect0x7 84b5df3
fix: parse HTML comment user IDs correctly
hect0x7 740f62c
test: support Python 3.9 logging handlers
hect0x7 046149e
fix: preserve comment page compatibility
hect0x7 3b258df
fix: refine comment pagination and progress notice
hect0x7 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,45 +1,104 @@ | ||
| """Build GitHub Release metadata from a release commit and changelog.""" | ||
|
|
||
| import ast | ||
| import os | ||
| import sys | ||
| import re | ||
| import sys | ||
| from pathlib import Path | ||
| from typing import Optional, Tuple | ||
|
|
||
|
|
||
| def add_output(k, v): | ||
| cmd = f'echo "{k}={v}" >> $GITHUB_OUTPUT' | ||
| print(cmd, os.system(cmd)) | ||
|
|
||
| ROOT_DIR = Path(__file__).resolve().parent.parent | ||
| VERSION_FILE = Path("src/jmcomic/__init__.py") | ||
| CHANGELOG_FILE = Path("CHANGELOG.md") | ||
| RELEASE_BODY_FILE = Path("release_body.txt") | ||
| RELEASE_SUBJECT_PATTERN = re.compile(r"^v(?P<version>\d+\.\d+\.\d+):(?:\s.*)?$") | ||
| VERSION_HEADING_PATTERN = re.compile( | ||
| r"^## \[(?P<version>[^]]+)] - (?P<date>\d{4}-\d{2}-\d{2})\s*$", | ||
| re.MULTILINE, | ||
| ) | ||
|
|
||
| def parse_body(body): | ||
| if ';' not in body: | ||
| return body | ||
|
|
||
| parts = body.split(";") | ||
| points = [] | ||
| for i, e in enumerate(parts): | ||
| e: str = e.strip() | ||
| if e == '': | ||
| def read_source_version(path: Path) -> str: | ||
| tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) | ||
| for node in tree.body: | ||
| if not isinstance(node, ast.Assign) or len(node.targets) != 1: | ||
| continue | ||
| points.append(f'{i + 1}. {e}') | ||
| target = node.targets[0] | ||
| if isinstance(target, ast.Name) and target.id == "__version__": | ||
| if isinstance(node.value, ast.Constant) and isinstance(node.value.value, str): | ||
| return node.value.value | ||
| raise ValueError(f"Static __version__ assignment not found in {path}") | ||
|
|
||
|
|
||
| def read_release_version(commit_message: str) -> str: | ||
| subject = commit_message.splitlines()[0].strip() if commit_message else "" | ||
| match = RELEASE_SUBJECT_PATTERN.fullmatch(subject) | ||
| if match is None: | ||
| raise ValueError(f"Release commit must match v{{version}}: summary, got: {subject}") | ||
| return match.group("version") | ||
|
|
||
|
|
||
| def extract_release_body(changelog: str, version: str) -> str: | ||
| matches = [match for match in VERSION_HEADING_PATTERN.finditer(changelog) if match.group("version") == version] | ||
| if not matches: | ||
| raise ValueError(f"Changelog section not found: ## [{version}] - YYYY-MM-DD") | ||
| if len(matches) > 1: | ||
| raise ValueError(f"Duplicate changelog sections found for version {version}") | ||
|
|
||
| match = matches[0] | ||
| next_heading = re.search(r"^## \[", changelog[match.end():], re.MULTILINE) | ||
| section_end = match.end() + next_heading.start() if next_heading else len(changelog) | ||
| body = changelog[match.end():section_end].strip() | ||
| if not body: | ||
| raise ValueError(f"Changelog section for version {version} is empty") | ||
| return body | ||
|
|
||
|
|
||
| def count_release_entries(body: str) -> int: | ||
| return sum(1 for line in body.splitlines() if line.lstrip().startswith("- ")) | ||
|
|
||
|
|
||
| return '\n'.join(points) | ||
| def build_release_metadata( | ||
| commit_message: Optional[str] = None, | ||
| root_dir: Optional[Path] = None, | ||
| ) -> Tuple[str, str]: | ||
| root_dir = root_dir or ROOT_DIR | ||
| source_version = read_source_version(root_dir / VERSION_FILE) | ||
| if commit_message is not None: | ||
| release_version = read_release_version(commit_message) | ||
| if release_version != source_version: | ||
| raise ValueError( | ||
| f"Version mismatch: release commit={release_version}, __init__.py={source_version}" | ||
| ) | ||
|
|
||
| changelog = (root_dir / CHANGELOG_FILE).read_text(encoding="utf-8") | ||
| return f"v{source_version}", extract_release_body(changelog, source_version) | ||
|
|
||
| def get_tag_and_body(): | ||
| msg = sys.argv[1] | ||
| print(f'msg: {msg}') | ||
| p = re.compile('(.*?): ?(.*)') | ||
| match = p.search(msg) | ||
| assert match is not None, f'commit message format is wrong: {msg}' | ||
| tag, body = match[1], match[2] | ||
| return body, tag | ||
|
|
||
| def add_output(key: str, value: str, output_path: Optional[str] = None) -> None: | ||
| output_path = output_path or os.environ.get("GITHUB_OUTPUT") | ||
| if output_path is None: | ||
| print(f"{key}={value}") | ||
| return | ||
| with Path(output_path).open("a", encoding="utf-8") as output_file: | ||
| output_file.write(f"{key}={value}\n") | ||
|
|
||
| def main(): | ||
| body, tag = get_tag_and_body() | ||
|
|
||
| add_output('tag', tag) | ||
| def main(commit_message: Optional[str] = None) -> int: | ||
| try: | ||
| tag, body = build_release_metadata(commit_message) | ||
| (ROOT_DIR / RELEASE_BODY_FILE).write_text(f"{body}\n", encoding="utf-8") | ||
| add_output("tag", tag) | ||
| except (OSError, SyntaxError, ValueError) as exc: | ||
| print(f"Release metadata error: {exc}", file=sys.stderr) | ||
| return 1 | ||
|
|
||
| with open('release_body.txt', 'w', encoding='utf-8') as f: | ||
| f.write(parse_body(body)) | ||
| print(f"Release version: {tag.removeprefix('v')}") | ||
| print(f"Changelog entries: {count_release_entries(body)}") | ||
| print(f"Release body source: {CHANGELOG_FILE}") | ||
| return 0 | ||
|
|
||
|
|
||
| main() | ||
| if __name__ == "__main__": | ||
| raise SystemExit(main(sys.argv[1] if len(sys.argv) >= 2 else None)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ Pillow | |
| psutil | ||
| pycryptodome | ||
| requests | ||
| rich | ||
| jm-view-server | ||
| zhconv | ||
| img2pdf | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: hect0x7/JMComic-Crawler-Python
Length of output: 222
🏁 Script executed:
Repository: hect0x7/JMComic-Crawler-Python
Length of output: 3084
🌐 Web query:
softprops action-gh-release v2 documentation permissions releases token write💡 Result:
To use softprops/action-gh-release, you must configure your GitHub Actions workflow with the necessary permissions to allow the action to create releases [1][2]. Required Permissions The action requires the contents: write permission [1][3]. If you also use the discussion_category_name input, you must additionally provide the discussions: write permission [1][3]. You can set these permissions at the workflow or job level in your YAML configuration: permissions: contents: write If enabling discussions: permissions: contents: write discussions: write Important Token Considerations 1. Default Token: By default, the action uses the standard GITHUB_TOKEN [2][4]. Ensure that your repository settings (under Actions > General > Workflow Permissions) allow for Read and Write access [5]. 2. Personal Access Token (PAT): If you are running the workflow on the release event (e.g., on: { release: { types: [published] } }), you must provide a personal access token instead of the default GITHUB_TOKEN, because the default token will not trigger subsequent workflows [1][2]. 3. Overriding: You can explicitly pass a custom token using the token input if needed [2][6]. A non-empty explicit token will override the default GITHUB_TOKEN [4][6]. Note that v2 of this action is no longer maintained [2]. While the permission requirements remain the same, it is recommended to review the project's current status for migration paths [2].
Citations:
🌐 Web query:
pypa gh-action-pypi-publish release/v1 OIDC token packages publish README💡 Result:
The pypa/gh-action-pypi-publish action (specifically the release/v1 branch) supports Trusted Publishing, which uses OpenID Connect (OIDC) to authenticate with PyPI without needing manual API tokens or passwords [1][2][3]. To use OIDC/Trusted Publishing with this action, you must follow these requirements: 1. Configure Trusted Publishing on PyPI: Your project on PyPI must already be configured to trust your GitHub repository as a publisher [1][3]. 2. Set Permissions: The GitHub Actions job running the publish step must have the id-token: write permission enabled [1][4]. 3. Omit Credentials: Do not provide an explicit username or password (or API token) to the action [1][2]. Example configuration for your GitHub Actions workflow: jobs: pypi-publish: name: Upload release to PyPI runs-on: ubuntu-latest environment: name: pypi permissions: id-token: write # Mandatory for Trusted Publishing steps: #... retrieve your distributions (e.g., download-artifact)... - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 Additionally, this action automatically generates and uploads PEP 740-compatible attestations using Sigstore, which uses the same OIDC identity [1][5]. If you need to use a self-hosted runner, Trusted Publishing is generally expected to work, provided it is configured correctly with GitHub [1][3].
Citations:
🏁 Script executed:
Repository: hect0x7/JMComic-Crawler-Python
Length of output: 3297
Pin the release actions to immutable commit SHAs.
softprops/action-gh-release@v2runs withcontents: write, andpypa/gh-action-pypi-publish@release/v1runs withid-token: writefor PyPI Trusted Publishing. Update both uses in.github/workflows/release.ymland.github/workflows/release_auto.ymlto reviewed full commit SHAs instead of mutable tags.🧰 Tools
🪛 zizmor (1.29.0)
[info] 29-29: action functionality is already included by the runner (superfluous-actions): use
gh releasein a script step(superfluous-actions)
🤖 Prompt for AI Agents