From ab5aa9c7ee3f822b0abd7c84722078ea8686019b Mon Sep 17 00:00:00 2001 From: vsoch Date: Fri, 16 Feb 2024 23:18:45 -0700 Subject: [PATCH] ci: add automated release pipeline Problem: we need to release in synx with flux-sched upstream Solution: have a release workflow that tests the current master, and tests and updates the version file and releases, coinciding with flux-sched. This will likely need further testing and tweaking as we decide on further customization, but should be a good start. Signed-off-by: vsoch --- .github/scripts/check-upstream-release.py | 146 ++++++++++++++++++++++ .github/workflows/release.yaml | 88 +++++++++++++ VERSION | 0 3 files changed, 234 insertions(+) create mode 100644 .github/scripts/check-upstream-release.py create mode 100644 .github/workflows/release.yaml create mode 100644 VERSION diff --git a/.github/scripts/check-upstream-release.py b/.github/scripts/check-upstream-release.py new file mode 100644 index 0000000..54c3c21 --- /dev/null +++ b/.github/scripts/check-upstream-release.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 + +# Check for upstream releases of flux-sched, and compare +# to current version here. If a new version is found, we + +import argparse +import requests +import sys +import os + +# python .github/scripts/check-upstream-release.py flux-framework/flux-sched + +token = os.environ.get("GITHUB_TOKEN") +headers = {} +if token: + headers["Authorization"] = "token %s" % token + + +def write_file(data, filename): + """ + Write content to file + """ + with open(filename, "w") as fd: + fd.writelines(data) + + +def read_file(filename): + """ + Read content from file + """ + with open(filename, "r") as fd: + content = fd.read() + return content + + +def set_env_and_output(name, value): + """ + helper function to echo a key/value pair to output and env. + + Parameters: + name (str) : the name of the environment variable + value (str) : the value to write to file + """ + for env_var in ("GITHUB_ENV", "GITHUB_OUTPUT"): + environment_file_path = os.environ.get(env_var) + print("Writing %s=%s to %s" % (name, value, env_var)) + + with open(environment_file_path, "a") as environment_file: + environment_file.write("%s=%s\n" % (name, value)) + + +class UpstreamUpdater: + def __init__(self, version_file, repo, dry_run=False): + self.version_file = os.path.abspath(version_file) + self.repo = repo + self.dry_run = dry_run + self._latest_version = None + self._current_version = self.get_current_version() + + @property + def current_version(self): + return self._current_version + + def get_current_version(self): + """ + Derive current version from file (or VERSION) + """ + if not os.path.exists(self.version_file): + sys.exit(f"{self.version_file} does not exist.") + self._current_version = read_file(self.version_file).strip("\n") + + def check(self): + """ + Given a repository name, check for new releases. + """ + latest = self.get_latest_release() + version = self.current_version + tag = latest["tag_name"] + + # Some versions are prefixed with v + if tag == version or tag == f"v{version}": + print("No new version found.") + return + print(f"New version {tag} detected!") + naked_version = tag.replace("v", "") + self.update_version_file(naked_version) + set_env_and_output("version", tag) + + def get_latest_release(self): + """ + Get the lateset release of a repository (under flux-framework) + """ + url = f"https://api.github.com/repos/{self.repo}/releases" + response = requests.get(url, headers=headers, params={"per_page": 100}) + response.raise_for_status() + + # latest release should be first + return response.json()[0] + + def update_version_file(self, version): + """ + Update the package file with a new version and digest. + """ + write_file(version, self.version_file) + + +def get_parser(): + parser = argparse.ArgumentParser( + description="Upstream Release Updater", + formatter_class=argparse.RawTextHelpFormatter, + ) + parser.add_argument( + "--version-file", + help="version file to parse", + default="VERSION", + dest="version_file", + ) + parser.add_argument( + "--repo", help="GitHub repository name", default="flux-framework/flux-sched" + ) + parser.add_argument( + "--dry-run", + action="store_true", + default=False, + help="Don't write changes to file", + ) + return parser + + +def main(): + parser = get_parser() + + # If an error occurs while parsing the arguments, the interpreter will exit with value 2 + args, extra = parser.parse_known_args() + + # Show args to the user + print("version file: %s" % args.version_file) + print(" repo: %s" % args.repo) + print(" dry-run: %s" % args.dry_run) + + updater = UpstreamUpdater(args.version_file, args.repo, args.dry_run) + updater.check() + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..cf913e1 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,88 @@ +name: release fluxion-go +on: + # Ensure we can trigger on demand + workflow_dispatch: + pull_request: [] + + # schedule runs on default branch + schedule: + - cron: '0 4 * * *' + +jobs: + + # We always test before release + test: + name: Test fluxion-go + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + test: [["fluxrm/flux-sched:jammy", "/usr/lib"], + ["fluxrm/flux-sched:fedora38", "/usr/lib64"], + ["fluxrm/flux-sched:bookworm-amd64", "/usr/lib"], + ["fluxrm/flux-sched:el8", "/usr/lib64"]] + + container: + image: ${{ matrix.test[0] }} + options: --user root + steps: + - uses: actions/checkout@v4 + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: ^1.19 + + - name: flux-sched build + run: git clone https://github.com/flux-framework/flux-sched /opt/flux-sched + - name: Build + run: LIB_PREFIX=${{ matrix.test[1] }} make build + - name: Test + run: LIB_PREFIX=${{ matrix.test[1] }} make test + + release: + runs-on: ubuntu-latest + needs: [test] + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - name: Check for New Releases + id: check + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + python -m pip install requests + python .github/scripts/check-upstream-release.py --repo flux-framework/flux-sched + + - name: Commit latest release version + if: (steps.check.outputs.version != '' && github.event_name != 'pull_request') + env: + version: ${{ steps.check.outputs.version }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "Found new version ${version}" + export BRANCH_FROM="release/${package}-${version}" + git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" + git branch + git config --global user.name "github-actions" + git config --global user.email "github-actions@users.noreply.github.com" + git config --global pull.rebase true + git add VERSION + if git diff-index --quiet HEAD --; then + printf "No changes\n" + else + printf "Changes\n" + today=$(date '+%Y-%m-%d') + git commit -a -m "Update for release ${today}" -m "Signed-off-by: github-actions " + git push origin main + fi + + - name: Release + if: (steps.check.outputs.version != '' && github.event_name != 'pull_request') + uses: softprops/action-gh-release@v1 + with: + name: goshare ${{ steps.check.outputs.version }} + tag_name: ${{ steps.check.outputs.version }} + body: "fluxion-go ${{ steps.check.outputs.version }}" + env: + GITHUB_REPOSITORY: flux-framework/fluxion-go diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..e69de29