def update_bundle(bundle_path): working_directory = os.path.abspath(os.getcwd()) os.chdir(bundle_path) git.submodule("foreach", "git", "fetch") # sh fails to find the subcommand so we use subprocess. subprocess.run(shlex.split( "git submodule foreach 'git checkout -q `git rev-list --tags --max-count=1`'" ), stdout=subprocess.DEVNULL) status = StringIO() result = git.status("--short", _out=status) updates = [] status = status.getvalue().strip() if status: for status_line in status.split("\n"): action, directory = status_line.split() if action != "M" or not directory.startswith("libraries"): raise RuntimeError("Unsupported updates") # Compute the tag difference. diff = StringIO() result = git.diff("--submodule=log", directory, _out=diff) diff_lines = diff.getvalue().split("\n") commit_range = diff_lines[0].split()[2] commit_range = commit_range.strip(":").split(".") old_commit = commit_to_tag(directory, commit_range[0]) new_commit = commit_to_tag(directory, commit_range[-1]) url = repo_remote_url(directory) summary = "\n".join(diff_lines[1:-1]) updates.append((url[:-4], old_commit, new_commit, summary)) os.chdir(working_directory) return updates
def update_bundle(bundle_path): working_directory = os.path.abspath(os.getcwd()) os.chdir(bundle_path) git.submodule("foreach", "git", "fetch") # Regular release tags are 'x.x.x'. Exclude tags that are alpha or beta releases. # They will contain a '-' in the tag, such as '3.0.0-beta.5'. # --exclude must be before --tags. # sh fails to find the subcommand so we use subprocess. subprocess.run(shlex.split( "git submodule foreach 'git checkout -q `git rev-list --exclude='*-*' --tags --max-count=1`'" ), stdout=subprocess.DEVNULL) status = StringIO() result = git.status("--short", _out=status) updates = [] status = status.getvalue().strip() if status: for status_line in status.split("\n"): action, directory = status_line.split() if directory.endswith("library_list.md"): continue if action != "M" or not directory.startswith("libraries"): raise RuntimeError("Unsupported updates") # Compute the tag difference. diff = StringIO() result = git.diff("--submodule=log", directory, _out=diff) diff_lines = diff.getvalue().split("\n") commit_range = diff_lines[0].split()[2] commit_range = commit_range.strip(":").split(".") old_commit = commit_to_tag(directory, commit_range[0]) new_commit = commit_to_tag(directory, commit_range[-1]) url = repo_remote_url(directory) summary = "\n".join(diff_lines[1:-1]) updates.append((url[:-4], old_commit, new_commit, summary)) os.chdir(working_directory) lib_list_updates = check_lib_links_md(bundle_path) if lib_list_updates: updates.append(( "https://github.com/adafruit/Adafruit_CircuitPython_Bundle/circuitpython_library_list.md", "NA", "NA", " > Added the following libraries: {}".format( ", ".join(lib_list_updates)))) return updates
def get_git_status(self) -> GitStatus: """ Returns Absolute Paths to all files that are staged Ignores files that are symlinks to directories """ import gitdb.exc # type: ignore repo = get_git_repo() if not repo or self._base_commit is None: return GitStatus([], [], [], []) try: repo.rev_parse(self._base_commit) except gitdb.exc.BadName: raise ActionFailure(f"Unknown git ref '{self._base_commit}'") # Output of git command will be relative to git project root status_output = zsplit( git.diff( "--cached", "--name-status", "--no-ext-diff", "-z", "--diff-filter=ACDMRTUXB", "--ignore-submodules", self._base_commit, ).stdout.decode()) added = [] modified = [] removed = [] unmerged = [] while status_output: code = status_output[0] fname = status_output[1] trim_size = 2 if not code.strip(): continue if code == StatusCode.Untracked or code == StatusCode.Ignored: continue resolved_name = self._fname_to_path(repo, fname) # If file is symlink to directory, skip absolute_name = Path(repo.working_tree_dir) / fname if absolute_name.is_symlink() and resolved_name.is_dir(): click.echo( f"| Skipping {absolute_name} since it is a symlink to a directory: {resolved_name}", err=True, ) else: # The following detection for unmerged codes comes from `man git-status` if code == StatusCode.Unmerged: unmerged.append(resolved_name) if (code[0] == StatusCode.Renamed ): # code is RXXX, where XXX is percent similarity removed.append(resolved_name) fname = status_output[2] trim_size += 1 added.append(resolved_name) if code == StatusCode.Added: added.append(resolved_name) if code == StatusCode.Modified: modified.append(resolved_name) if code == StatusCode.Deleted: removed.append(resolved_name) status_output = status_output[trim_size:] debug_echo( f"Git status:\nadded: {added}\nmodified: {modified}\nremoved: {removed}\nunmerged: {unmerged}" ) return GitStatus(added, modified, removed, unmerged)
def new_release(bundle, bundle_path): working_directory = os.path.abspath(os.getcwd()) os.chdir(bundle_path) print(bundle) current_release = github.get( "/repos/adafruit/{}/releases/latest".format(bundle)) last_tag = current_release.json()["tag_name"] contributors = get_contributors("adafruit/" + bundle, last_tag + "..") added_submodules = [] updated_submodules = [] repo_links = {} output = StringIO() git.diff("--submodule=short", last_tag + "..", _out=output) output = output.getvalue().strip() if not output: print("Everything is already released.") return current_submodule = None current_index = None for line in output.split("\n"): if line.startswith("diff"): current_submodule = line.split()[-1][len("b/"):] continue elif "index" in line: current_index = line continue elif not line.startswith("+Subproject"): continue # We have a candidate submodule change. directory = current_submodule commit_range = current_index.split()[1] library_name = directory.split("/")[-1] if commit_range.startswith("0000000"): added_submodules.append(library_name) commit_range = commit_range.split(".")[-1] elif commit_range.endswith("0000000"): # For now, skip documenting deleted modules. continue else: updated_submodules.append(library_name) repo_url = repo_remote_url(directory) new_commit = commit_range.split(".")[-1] release_tag = commit_to_tag(directory, new_commit) with Submodule(directory): submodule_contributors = get_contributors(repo_name(repo_url), commit_range) add_contributors(contributors, submodule_contributors) repo_links[library_name] = repo_url[:-4] + "/releases/" + release_tag release_description = [] if added_submodules: additions = [] for library in added_submodules: additions.append("[{}]({})".format(library, repo_links[library])) release_description.append("New libraries: " + ", ".join(additions)) if updated_submodules: updates = [] for library in updated_submodules: updates.append("[{}]({})".format(library, repo_links[library])) release_description.append("Updated libraries: " + ", ".join(updates)) release_description.append("") contributors = sorted(contributors, key=contributors.__getitem__, reverse=True) contributors = ["@" + x for x in contributors] release_description.append("As always, thank you to all of our contributors: " + ", ".join(contributors)) release_description.append("\n--------------------------\n") release_description.append("The libraries in each release are compiled for all recent major versions of CircuitPython. Please download the one that matches the major version of your CircuitPython. For example, if you are running 4.0.0 you should download the `4.x` bundle.\n") release_description.append("To install, simply download the matching zip file, unzip it, and selectively copy the libraries you would like to install into the lib folder on your CIRCUITPY drive. This is especially important for non-express boards such as the [Trinket M0](https://www.adafruit.com/product/3500), [Gemma M0](https://www.adafruit.com/product/3501) and [Feather M0 Basic](https://www.adafruit.com/product/2772).") release = { "tag_name": "{0:%Y%m%d}".format(date.today()), "target_commitish": repo_sha(), "name": "{0:%B} {0:%d}, {0:%Y} auto-release".format(date.today()), "body": "\n".join(release_description), "draft": False, "prerelease": False} print("Releasing {}".format(release["tag_name"])) print(release["body"]) response = github.post("/repos/adafruit/" + bundle + "/releases", json=release) if not response.ok: print("Failed to create release") print(release) print(response.request.url) print(response.text) os.chdir(working_directory)
def get_git_status(self) -> GitStatus: """ Returns Absolute Paths to all files that are staged """ import gitdb.exc # type: ignore repo = get_git_repo() if not repo or self._base_commit is None: return GitStatus([], [], [], []) try: repo.rev_parse(self._base_commit) except gitdb.exc.BadName: raise RuntimeError(f"Unknown git ref '{self._base_commit}'") # Output of git command will be relative to git project root status_output = zsplit( git.diff( "--name-status", "--no-ext-diff", "-z", "--diff-filter=ACDMRTUXB", "--ignore-submodules", self._base_commit, ).stdout.decode()) added = [] modified = [] removed = [] unmerged = [] while status_output: code = status_output[0] fname = status_output[1] trim_size = 2 if not code.strip(): continue if code == StatusCode.Untracked or code == StatusCode.Ignored: continue # The following detection for unmerged codes comes from `man git-status` if code == StatusCode.Unmerged: unmerged.append(self._fname_to_path(repo, fname)) if (code[0] == StatusCode.Renamed ): # code is RXXX, where XXX is percent similarity removed.append(self._fname_to_path(repo, fname)) fname = status_output[2] trim_size += 1 added.append(self._fname_to_path(repo, fname)) if code == StatusCode.Added: added.append(self._fname_to_path(repo, fname)) if code == StatusCode.Modified: modified.append(self._fname_to_path(repo, fname)) if code == StatusCode.Deleted: removed.append(self._fname_to_path(repo, fname)) status_output = status_output[trim_size:] debug_echo( f"Git status:\nadded: {added}\nmodified: {modified}\nremoved: {removed}\nunmerged: {unmerged}" ) return GitStatus(added, modified, removed, unmerged)