diff --git a/docs/guides/documentation/releases/README.md b/docs/guides/documentation/releases/README.md new file mode 100644 index 000000000..798a88523 --- /dev/null +++ b/docs/guides/documentation/releases/README.md @@ -0,0 +1,94 @@ +# Releases + +
Streamline software releases through consistent templates, automated aggregation and notifications.
+ +## Introduction + +**Background**: Making releases can be a chore. From ensuring the release notes have helpful information to aggregating many updates and notifying your users - doing the process right can be time consuming and fraught with misunderstandings. In this guide we recommend a standardized, automated way to notify your users of new releases. For example, we recommend using a `.github/release.yml` template to allow for more readable release notes, showcasing the importance of each update. We also provide automation for aggregating releases from many repositories into a super-release that provides a holistic view of project progress. Finally, we recommend automated notifications through GitHub integrations to help keep everyone informed. + +**Use Cases**: +- Automating release notes generation with a readable template +- Aggregating multiple repository releases for projects with many repositories +- Streamlining release notifications to keep teams and users informed automatically + +--- + +## Prerequisites +* A GitHub account and repository +* Basic understanding of GitHub Actions and workflows +* Access to IM channels like Slack for integration + +--- + +## Quick Start +**[⬇️ Recommended GitHub release.yml Template](release.yml)** + +_A customizable GitHub-specific release template. Customize it to fit your project's labeling scheme and presentation preferences for release notes and place it in your GitHub repository's `.github/release.yml` path._ + +**[⬇️ Python Script to Aggregate GitHub Releases](gh_aggregate_release_notes.py)** + +_A Python script that aggregates release notes from many repositories and generates a "super" release text._ + +NOTE: You'll need a configuration file to use the above Python script. See step 2.2 below for an example file that you can customize for your project. + + +--- + +## Step-by-Step Guide + +1. **Customize Release Notes**: + - Create a `.github/release.yml` file in your repository. + - Download our [GitHub release notes template](release.yml) and place the contents into your `.github/release.yml` file. + - Customize our recommended template as a baseline and to configure how release notes are generated based on your project's issue labels. + +2. **Aggregate Releases**: + We recommend using a script to automatically aggregate release notes from multiple repositories. Details below. + + 2.1 Download our script + - Copy/download our script to your local machine + ``` + curl --output gh_aggregate_release_notes.py https://raw.githubusercontent.com/NASA-AMMOS/slim/issue-97/docs/guides/documentation/releases/gh_aggregate_release_notes.py + ``` + + 2.2 Create a configuration file with your project's release information. Save the file with extension `.yml` A sample is provided below: + ``` + github_token: + urls: + - https://github.com/your-org/your-repo-1/releases/tag/v1.1.0 + - https://github.com/your-org/your-repo-2/releases/tag/v2.5.3 + ``` + 2.3 Run the script to generate aggregated release notes + ``` + $ python gh_aggregate_release_notes.py your_configuration_file.yml + ``` + + 2.3 Review your aggregated release notes + - Your aggregated release notes should be printed to standard output (`stdout`) + +3. **Set Up Notifications**: + - Integrate GitHub with your IM channels, such as Slack, for example installing and configuring extensions like: [GitHub's Slack integration](https://slack.github.com). + - Conduct individual outreach to your customers/users to encourage them to watch your repository for release updates to stay informed about new releases through GitHub's notification system. + +--- + +## Frequently Asked Questions (FAQ) + +- Q: Can I customize which pull requests or issues appear in the release notes? +- A: Yes, by using labels and the `exclude-labels` field in the `.github/release.yml` file, you can control the inclusion of pull requests and issues in your release notes. + +--- + +## Credits + +**Authorship**: +- [@riverma](https://www.github.com/riverma) + +**Acknowledgements**: +* [@hookhua](https://github.com/hookhua) for inspiration to make this guide. +* [HySDS project](https://github.com/hysds) for inspiration and feedback. + +--- + +## Feedback and Contributions + +We welcome feedback and contributions to help improve and grow this page. Please see our [contribution guidelines](https://nasa-ammos.github.io/slim/docs/contribute/contributing/). diff --git a/docs/guides/documentation/releases/gh_aggregate_release_notes.py b/docs/guides/documentation/releases/gh_aggregate_release_notes.py new file mode 100644 index 000000000..3e409bdbb --- /dev/null +++ b/docs/guides/documentation/releases/gh_aggregate_release_notes.py @@ -0,0 +1,99 @@ +import requests +import yaml +import argparse +from urllib.parse import urlparse +from time import sleep +from tqdm import tqdm +import random + +def read_config(file_path): + """ + Reads the YAML configuration file and returns its contents. + + Args: + file_path (str): The path to the YAML configuration file. + + Returns: + dict: The contents of the YAML file if successful, None otherwise. + """ + with open(file_path, 'r') as stream: + try: + return yaml.safe_load(stream) + except yaml.YAMLError as exc: + print(exc) + return None + +def get_release_notes(url, github_token): + """ + Extracts the owner, repo, and tag from the given GitHub URL and retrieves the release notes using the GitHub API. + Implements retries with exponential backoff and jitter for handling API rate limits and network issues. + + Args: + url (str): The URL of the GitHub release tag page. + github_token (str): The GitHub token used for authentication. + + Returns: + str: The release notes text if successful, empty string otherwise. + """ + parsed_url = urlparse(url) + hostname = parsed_url.hostname + path_parts = parsed_url.path.split('/') + owner, repo, tag = path_parts[1], path_parts[2], path_parts[-1] + + # Be agnostic to GitHub.com or GitHub Enterprise repo URLs + api_url_prefix = f"https://api.github.com/repos/{owner}/{repo}" if hostname == "github.com" else f"https://{hostname}/api/v3/repos/{owner}/{repo}" + api_url = f"{api_url_prefix}/releases/tags/{tag}" + headers = { + "Authorization": f"token {github_token}", + "Accept": "application/vnd.github.v3+json" + } + + max_retries = 5 + retry_num = 0 + backoff_factor = 2 + while retry_num < max_retries: + response = requests.get(api_url, headers=headers) + if response.status_code == 200: + return response.json().get("body", "") + else: + print(f"Error fetching release notes for {url}: {response.status_code}. Retrying...") + sleep_time = backoff_factor * (2 ** retry_num) + random.uniform(0, 1) + sleep(sleep_time) + retry_num += 1 + + print(f"Failed to fetch release notes after {max_retries} attempts.") + return "" + +def main(config_file): + """ + Main function to aggregate GitHub release notes. + It reads a YAML configuration file for GitHub repository URLs and a GitHub token, + then retrieves and prints the release notes for each repository URL listed. + + Args: + config_file (str): The path to the YAML configuration file containing the GitHub token and URLs. + """ + config = read_config(config_file) + if not config: + print("Failed to read configuration.") + return + + github_token = config.get("github_token") + urls = config.get("urls", []) + + release_notes = "" + + for url in tqdm(urls, desc="Downloading release notes"): + notes = get_release_notes(url, github_token) + if notes: + repo_name = url.split("/")[4] # Assumes a specific URL structure. + release_notes += f"# {repo_name}\n\n{notes}\n\n" + + print(release_notes) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Aggregates GitHub release notes from a configuration file.") + parser.add_argument('config_file', type=str, help="Path to the YAML configuration file") + args = parser.parse_args() + + main(args.config_file) diff --git a/docs/guides/documentation/releases/release.yml b/docs/guides/documentation/releases/release.yml new file mode 100644 index 000000000..a81182e60 --- /dev/null +++ b/docs/guides/documentation/releases/release.yml @@ -0,0 +1,29 @@ +# place in .github/release.yml + +changelog: + exclude: # exclude any PRs labels we don't want in the changelog + labels: + - ignore-for-release + - skip-changelog + categories: # group PRs by your most important labels. Add / customize as needed. + - title: "🚀 Features" + labels: + - enhancement + - title: "🐛 Bug Fixes" + labels: + - bug + - title: "📚 Documentation" + labels: + - documentation + - title: 📦 Dependencies + labels: + - dependencies + - title: 💻 Other Changes + labels: + - "*" + exclude: # place the list of all labels you've categorized above to avoid duplication! + labels: + - enhancement + - bug + - documentation + - dependencies \ No newline at end of file