From 044d5287ea7e23d061d3118097addb31a27c06d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 3 Nov 2023 20:53:48 +0400 Subject: [PATCH 1/4] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Separate=20label=20sec?= =?UTF-8?q?tion=20header=20prefix=20from=20label=20text,=20with=20`label?= =?UTF-8?q?=5Fheader=5Fprefix`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- latest_changes/main.py | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/latest_changes/main.py b/latest_changes/main.py index 77c473d..9198f1a 100644 --- a/latest_changes/main.py +++ b/latest_changes/main.py @@ -28,17 +28,17 @@ class Settings(BaseSettings): input_end_regex: str = "^### " input_debug_logs: Optional[bool] = False input_labels: List[Section] = [ - Section(label="breaking", header="#### Breaking Changes"), - Section(label="security", header="#### Security Fixes"), - Section(label="feature", header="#### Features"), - Section(label="bug", header="#### Fixes"), - Section(label="refactor", header="#### Refactors"), - Section(label="upgrade", header="#### Upgrades"), - Section(label="docs", header="#### Docs"), - Section(label="lang-all", header="#### Translations"), - Section(label="internal", header="#### Internal"), + Section(label="breaking", header="Breaking Changes"), + Section(label="security", header="Security Fixes"), + Section(label="feature", header="Features"), + Section(label="bug", header="Fixes"), + Section(label="refactor", header="Refactors"), + Section(label="upgrade", header="Upgrades"), + Section(label="docs", header="Docs"), + Section(label="lang-all", header="Translations"), + Section(label="internal", header="Internal"), ] - input_next_section_start: str = "^#### " + input_label_header_prefix: str = "#### " class PartialGitHubEventInputs(BaseModel): @@ -108,11 +108,15 @@ def generate_content( sections: list[SectionContent] = [] sectionless_content = "" for label in settings.input_labels: - label_match = re.search(label.header, release_content, flags=re.MULTILINE) + label_match = re.search( + f"^{settings.input_label_header_prefix}{label.header}", + release_content, + flags=re.MULTILINE, + ) if not label_match: continue next_label_match = re.search( - settings.input_next_section_start, + f"^{settings.input_label_header_prefix}", release_content[label_match.end() :], flags=re.MULTILINE, ) @@ -161,7 +165,7 @@ def generate_content( if sectionless_content: new_release_content = f"{sectionless_content}" use_sections = [ - f"{section.header}\n\n{section.content}" + f"{settings.input_label_header_prefix}{section.header}\n\n{section.content}" for section in new_sections if section.content ] @@ -172,7 +176,10 @@ def generate_content( else: new_release_content = updated_content - new_content = f"{pre_header_content}\n\n{new_release_content}\n\n{post_release_content}".strip() + "\n" + new_content = ( + f"{pre_header_content}\n\n{new_release_content}\n\n{post_release_content}".strip() + + "\n" + ) return new_content From e92d10798171a844663570de027a766b255ed63c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 3 Nov 2023 20:54:33 +0400 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=85=20Update=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_generate_content.py | 40 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/test_generate_content.py b/tests/test_generate_content.py index 86ecbd8..2fa686b 100644 --- a/tests/test_generate_content.py +++ b/tests/test_generate_content.py @@ -830,18 +830,18 @@ def test_multiple_header_sections(): input_labels=cast( Any, [ - {"label": "breaking", "header": "### Breaking Changes"}, - {"label": "security", "header": "### Security Fixes"}, - {"label": "feature", "header": "### Features"}, - {"label": "bug", "header": "### Fixes"}, - {"label": "refactor", "header": "### Refactors"}, - {"label": "upgrade", "header": "### Upgrades"}, - {"label": "docs", "header": "### Docs"}, - {"label": "lang-all", "header": "### Translations"}, - {"label": "internal", "header": "### Internal"}, + {"label": "breaking", "header": "Breaking Changes"}, + {"label": "security", "header": "Security Fixes"}, + {"label": "feature", "header": "Features"}, + {"label": "bug", "header": "Fixes"}, + {"label": "refactor", "header": "Refactors"}, + {"label": "upgrade", "header": "Upgrades"}, + {"label": "docs", "header": "Docs"}, + {"label": "lang-all", "header": "Translations"}, + {"label": "internal", "header": "Internal"}, ], ), - input_next_section_start="^### ", + input_label_header_prefix="### ", ) pr = TemplateDataPR( title="Demo PR", @@ -968,18 +968,18 @@ def test_multiple_header_sections_label(): input_labels=cast( Any, [ - {"label": "breaking", "header": "### Breaking Changes"}, - {"label": "security", "header": "### Security Fixes"}, - {"label": "feature", "header": "### Features"}, - {"label": "bug", "header": "### Fixes"}, - {"label": "refactor", "header": "### Refactors"}, - {"label": "upgrade", "header": "### Upgrades"}, - {"label": "docs", "header": "### Docs"}, - {"label": "lang-all", "header": "### Translations"}, - {"label": "internal", "header": "### Internal"}, + {"label": "breaking", "header": "Breaking Changes"}, + {"label": "security", "header": "Security Fixes"}, + {"label": "feature", "header": "Features"}, + {"label": "bug", "header": "Fixes"}, + {"label": "refactor", "header": "Refactors"}, + {"label": "upgrade", "header": "Upgrades"}, + {"label": "docs", "header": "Docs"}, + {"label": "lang-all", "header": "Translations"}, + {"label": "internal", "header": "Internal"}, ], ), - input_next_section_start="^### ", + input_label_header_prefix="### ", ) pr = TemplateDataPR( title="Demo PR", From d89eafc4f309a808c115d7e9b0ccd27ed8e246a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 3 Nov 2023 20:56:55 +0400 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=93=9D=20Update=20README=20with=20lab?= =?UTF-8?q?el=5Fheader=5Fprefix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 87 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index d370314..75da96b 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: docker://tiangolo/latest-changes:0.1.0 + - uses: docker://tiangolo/latest-changes:0.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} ``` @@ -39,9 +39,9 @@ jobs: **Note**: you can also use the GitHub action directly instead of with Docker, but that would take an extra minute: ```YAML - # - uses: docker://tiangolo/latest-changes:0.1.0 + # - uses: docker://tiangolo/latest-changes:0.2.0 # This is slower but also works - - uses: tiangolo/latest-changes@0.1.0 + - uses: tiangolo/latest-changes@0.2.0 ``` In this minimal example, it uses all the default configurations. @@ -79,15 +79,15 @@ You can also use labels in the PRs to configure which sections they should show By default, it will use these labels and headers: -* `breaking`: `#### Breaking Changes` -* `security`: `#### Security Fixes` -* `feature`: `#### Features` -* `bug`: `#### Fixes` -* `refactor`: `#### Refactors` -* `upgrade`: `#### Upgrades` -* `docs`: `#### Docs` -* `lang-all`: `#### Translations` -* `internal`: `#### Internal` +* `breaking`: `Breaking Changes` +* `security`: `Security Fixes` +* `feature`: `Features` +* `bug`: `Fixes` +* `refactor`: `Refactors` +* `upgrade`: `Upgrades` +* `docs`: `Docs` +* `lang-all`: `Translations` +* `internal`: `Internal` So, if you have a PR with a label `feature`, by default, it will show up in the section about features, like: @@ -97,15 +97,9 @@ So, if you have a PR with a label `feature`, by default, it will show up in the > > * ✨ Add support for Jinja2 templates for latest changes messages. PR [#23](https://github.com/tiangolo/latest-changes/pull/23) by [@tiangolo](https://github.com/tiangolo). -You can configure the labels and headers used in the GitHub Action `labels` workflow configuration. +You can configure the labels and headers used in the GitHub Action `labels` workflow configuration, and you can configure the header prefix, by default `#### `. -It takes a JSON array of JSON objects that contain a key `label` with the label you would add to each PR, and a key `header` with the header text that should be added to the release notes for that label. - -The order is important, the first label from the list that is found in your PR is the one that will be used. So, if you have a PR that has both labels `feature` and `bug`, if you use the default configuration, it will show up in the section for features as that comes first, if you want it to show up in the section for bugs you would need to change the order of the list of this configuration to have `bug` first. - -Note that this JSON has to be passed as a string because that's the only thing that GitHub Actions support for configurations. - -See the example below in the configuration section. +Read more about it in the section about configuration. ## Existing PRs - Running Manually @@ -128,10 +122,20 @@ You can configure: * `latest_changes_file`: The file to modify with the latest changes. For example: `./docs/latest-changes.rst`. * `latest_changes_header`: The header to look for before adding a new message. for example: `# CHANGELOG`. * `template_file`: A custom Jinja2 template file to use to generate the message, you could use this to generate a different message or to use a different format, for example, HTML instead of the default Markdown. -* `end_regex`: A RegEx string that marks the end of this release, so it normally matches the start of the header of the next release section, normally the same header level as `latest_changes_header`, so, if the `latest_changes_header` is `### Latest Changes`, the content for the next release below is probably something like `### 0.2.0`, then the `end_regex` should be `^### `. +* `end_regex`: A RegEx string that marks the end of this release, so it normally matches the start of the header of the next release section, normally the same header level as `latest_changes_header`, so, if the `latest_changes_header` is `### Latest Changes`, the content for the next release below is probably something like `### 0.2.0`, then the `end_regex` should be `^### `. This is used to limit the content updated as this will read the existing sub sections and possibly update them using the labels configuration and the labels in the PR. * `debug_logs`: Set to `'true'` to show logs with the current settings. * `labels`: A JSON array of JSON objects with a `label` that you would put in each PR and the `header` that would be used in the release notes. See the example below. -* `next_section_start`: A RegEx for the start of the next label header section. If the headers start with `#### ` (as in `#### Features`), then this RegEx should match that, like `^#### `. +* `label_header_prefix`: A prefix to put before each label's header. This is also used to detect where the next label header starts. By default it is `#### `, so the headers will look like `#### Features`. + +### Configuring Labels + +The `labels` configuration takes a JSON array of JSON objects that contain a key `label` with the label you would add to each PR, and a key `header` with the header text that should be added to the release notes for that label. + +The order is important, the first label from the list that is found in your PR is the one that will be used. So, if you have a PR that has both labels `feature` and `bug`, if you use the default configuration, it will show up in the section for features, as that comes first. If you want it to show up in the section for bugs you would need to change the order of the list of this configuration to have `bug` first. + +Note that this JSON has to be passed as a string because that's the only thing that GitHub Actions support for configurations. + +If you want to keep the same default labels but change the header level, so, add or remove hash symbols, you can set the `label_header_prefix` configuration. You could also use it to set a different header prefix, but the common case is changing the section header level. ## Configuration example @@ -169,7 +173,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: tiangolo/latest-changes@0.1.0 + - uses: tiangolo/latest-changes@0.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} latest_changes_file: docs/release-notes.md @@ -183,19 +187,20 @@ jobs: # We also add a custom last label "egg" for PRs with easter eggs. labels: > [ - {"label": "breaking", "header": "### Breaking Changes"}, - {"label": "security", "header": "### Security Fixes"}, - {"label": "feature", "header": "### Features"}, - {"label": "bug", "header": "### Fixes"}, - {"label": "refactor", "header": "### Refactors"}, - {"label": "upgrade", "header": "### Upgrades"}, - {"label": "docs", "header": "### Docs"}, - {"label": "lang-all", "header": "### Translations"}, - {"label": "internal", "header": "### Internal"}, - {"label": "egg", "header": "### Easter Eggs"} + {"label": "breaking", "header": "Breaking Changes"}, + {"label": "security", "header": "Security Fixes"}, + {"label": "feature", "header": "Features"}, + {"label": "bug", "header": "Fixes"}, + {"label": "refactor", "header": "Refactors"}, + {"label": "upgrade", "header": "Upgrades"}, + {"label": "docs", "header": "Docs"}, + {"label": "lang-all", "header": "Translations"}, + {"label": "internal", "header": "Internal"}, + {"label": "egg", "header": "Easter Eggs"} ] - # This should match the start of the label headers - next_section_start: '^### ' + # This will be added to the start of each label's header and + # will be used to detect existing label headers + label_header_prefix: '### ' ``` In this custom config: @@ -204,13 +209,13 @@ In this custom config: * It uses the GitHub action directly: ``` -tiangolo/latest-changes@0.1.0 +tiangolo/latest-changes@0.2.0 ``` instead of with Docker: ``` -docker://tiangolo/latest-changes:0.1.0 +docker://tiangolo/latest-changes:0.2.0 ``` **Note**: that would make every run about 1 min slower, but you can do that if you prefer it 🤷. @@ -222,7 +227,7 @@ docker://tiangolo/latest-changes:0.1.0 # Release Notes ``` -**Note**: The `latest_changes_header` is a [regular expression](https://regex101.com/). In this case it has two newlines, and the message will be added right after that (without adding an extra newline). +**Note**: The `latest_changes_header` is a [regular expression](https://regex101.com/). So it will generate messages like: @@ -242,9 +247,9 @@ And that Markdown will be shown like: * It will show a lot of debugging information. -* It will use the same default labels and headers plus another one for easter eggs, but with 3 hash symbols instead of the default of 4. +* It will use the same default labels and headers plus another one for easter eggs. -* It will detect the start of each header section (the ones from the labels) with the regular expression `^### `. +* It will show those section headers from labels with 3 hash symbols instead of the default of 4. And it will also find any existing header checking for that prefix (it will use a regular expression like `^### `). ## Protected Branches @@ -293,7 +298,7 @@ jobs: - uses: actions/checkout@v4 with: token: ${{ secrets.ACTIONS_TOKEN }} - - uses: docker://tiangolo/latest-changes:0.1.0 + - uses: docker://tiangolo/latest-changes:0.2.0 with: token: ${{ secrets.GITHUB_TOKEN }} ``` From 9930f3a5fd72dae418eecf04494b4d67880f7f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 3 Nov 2023 20:57:21 +0400 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=94=A7=20Update=20GitHub=20Action=20c?= =?UTF-8?q?onfig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- action.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/action.yml b/action.yml index 6f63217..2a4e748 100644 --- a/action.yml +++ b/action.yml @@ -33,19 +33,19 @@ inputs: required: false default: > [ - {"label": "breaking", "header": "#### Breaking Changes"}, - {"label": "security", "header": "#### Security Fixes"}, - {"label": "feature", "header": "#### Features"}, - {"label": "bug", "header": "#### Fixes"}, - {"label": "refactor", "header": "#### Refactors"}, - {"label": "upgrade", "header": "#### Upgrades"}, - {"label": "docs", "header": "#### Docs"}, - {"label": "lang-all", "header": "#### Translations"}, - {"label": "internal", "header": "#### Internal"} + {"label": "breaking", "header": "Breaking Changes"}, + {"label": "security", "header": "Security Fixes"}, + {"label": "feature", "header": "Features"}, + {"label": "bug", "header": "Fixes"}, + {"label": "refactor", "header": "Refactors"}, + {"label": "upgrade", "header": "Upgrades"}, + {"label": "docs", "header": "Docs"}, + {"label": "lang-all", "header": "Translations"}, + {"label": "internal", "header": "Internal"} ] - next_section_start: - description: A RegEx for the start of the next label header section. If the headers start with `#### ` (as in `#### Features`), then this RegEx should match that, like `^#### `. - default: '^#### ' + label_header_prefix: + description: A prefix to put before each label's header. This is also used to detect where the next label header starts. By default it is `#### `, so the headers will look like `#### Features`. + default: '#### ' runs: using: docker image: Dockerfile