diff --git a/.github/workflows/image-check.yaml b/.github/workflows/image-check.yaml index 96156261..ff54016b 100644 --- a/.github/workflows/image-check.yaml +++ b/.github/workflows/image-check.yaml @@ -1,6 +1,8 @@ name: Image-check on: pull_request: + branches: + - v* jobs: test: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 039f62ae..6a4ec217 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,8 @@ Dockerfile.cross .odo *.code-workspace *.vscode + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class diff --git a/config/base/params.env b/config/base/params.env index 6b396a3d..499233cb 100644 --- a/config/base/params.env +++ b/config/base/params.env @@ -1,12 +1,12 @@ -IMAGES_APISERVER=quay.io/opendatahub/ds-pipelines-api-server@sha256:4650c62254cd79112de3e4f09270130501d0d86a4dea79b74c2fcb8b5ca567e7 -IMAGES_ARTIFACT=quay.io/opendatahub/ds-pipelines-artifact-manager@sha256:58a13845901f8aae5421f640eeebee0abf3b12b27c1f96fbc8ff199b7e4f8d8d -IMAGES_PERSISTENTAGENT=quay.io/opendatahub/ds-pipelines-persistenceagent@sha256:c8b0953c28fd24180ddd24a30c68df411d299ccc7f6bc18ab15f4dba4a84b7d9 -IMAGES_SCHEDULEDWORKFLOW=quay.io/opendatahub/ds-pipelines-scheduledworkflow@sha256:31d049e74ab038f3a6d3ff9fa8953a4d0ddb21b0efc43fbb5b07fbaf83817022 -IMAGES_MLMDENVOY=quay.io/opendatahub/ds-pipelines-metadata-envoy@sha256:f2d5d430bbc925520f635f35698e604aae391ace39b15a5d601a9c9eb26dec2b -IMAGES_MLMDGRPC=quay.io/opendatahub/ds-pipelines-metadata-grpc@sha256:2490aadb2227cc72fd9e698549a8cd3270b669a2faa24bb0603c37f1c71ac8c4 -IMAGES_MLMDWRITER=quay.io/opendatahub/ds-pipelines-metadata-writer@sha256:89fc26374f8e58384628f6b178eb9b8e3ebb111fe395c529d0b65ba8adaa89f5 -IMAGES_DSPO=quay.io/opendatahub/data-science-pipelines-operator@sha256:c1d77b668149396a4409926eea279647c817a02868a3d21f9a4b5f30c1e86766 -IMAGES_CACHE=registry.access.redhat.com/ubi8/ubi-minimal@sha256:e52fc1de73dc2879516431ff1865e0fb61b1a32f57b6f914bdcddb13c62f84e6 -IMAGES_MOVERESULTSIMAGE=registry.access.redhat.com/ubi8/ubi-micro@sha256:443db9a646aaf9374f95d266ba0c8656a52d70d0ffcc386a782cea28fa32e55d -IMAGES_MARIADB=registry.redhat.io/rhel8/mariadb-103@sha256:6c3ae581b754017b335a70388c0010cf729df8a29daeb6651642ebee4e8abfde +IMAGES_APISERVER=quay.io/opendatahub/ds-pipelines-api-server:latest +IMAGES_ARTIFACT=quay.io/opendatahub/ds-pipelines-artifact-manager:latest +IMAGES_PERSISTENTAGENT=quay.io/opendatahub/ds-pipelines-persistenceagent:latest +IMAGES_SCHEDULEDWORKFLOW=quay.io/opendatahub/ds-pipelines-scheduledworkflow:latest +IMAGES_MLMDENVOY=quay.io/opendatahub/ds-pipelines-metadata-envoy:latest +IMAGES_MLMDGRPC=quay.io/opendatahub/ds-pipelines-metadata-grpc:latest +IMAGES_MLMDWRITER=quay.io/opendatahub/ds-pipelines-metadata-writer:latest +IMAGES_DSPO=quay.io/opendatahub/data-science-pipelines-operator:latest +IMAGES_CACHE=registry.access.redhat.com/ubi8/ubi-minimal:8.8 +IMAGES_MOVERESULTSIMAGE=registry.access.redhat.com/ubi8/ubi-micro:8.8 +IMAGES_MARIADB=registry.redhat.io/rhel8/mariadb-103:1 IMAGES_OAUTHPROXY=registry.redhat.io/openshift4/ose-oauth-proxy@sha256:ab112105ac37352a2a4916a39d6736f5db6ab4c29bad4467de8d613e80e9bb33 diff --git a/docs/release/compatibility.md b/docs/release/compatibility.md new file mode 100644 index 00000000..1fef5e2f --- /dev/null +++ b/docs/release/compatibility.md @@ -0,0 +1,36 @@ + +# DSP Version Compatibility Table + +This is an auto generated DSP version compatibility table. +Each row outlines the versions for individual subcomponents and images that are leveraged within DSP. + +For some components, the versions match with their respective image tags within their respective Quay, GCR, or RedHat image +registries, this is true for the following: + +* [ml-metadata] +* [envoy] +* [oauth-proxy] + * for Oauth Proxy DSP follows the same version digest as the Oauth Proxy leveraged within the rest of ODH. +* [mariaDB] + * for MariaDB the entire column represents different tag versions for MariDB Version 10.3, DSP follows the latest digest for the `1` tag + for each DSP release. +* [ubi-minimal] + * Used for default base images during Pipeline Runs +* [ubi-micro] + * Used for default cache image for runs + + +| dsp | kfp-tekton | ml-metadata | envoy | ocp-pipelines | oauth-proxy | mariadb-103 | ubi-minimal | ubi-micro | openshift | +|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----| +| 1.0.x | 1.5.1 | 1.5.0 | 1.8.4 | v4.10 | v4.12 | 1 | 8.8 | 8.8 | 4.10,4.11,4.12 | +| 1.1.x | 1.5.1 | 1.5.0 | 1.8.4 | v4.10 | v4.12 | 1 | 8.8 | 8.8 | 4.10,4.11,4.12 | +| 1.2.x | 1.5.1 | 1.5.0 | 1.8.4 | v4.10 | v4.10 | 1 | 8.8 | 8.8 | 4.10,4.11,4.12 | + + + +[ml-metadata]: https://github.com/opendatahub-io/data-science-pipelines/blob/master/third-party/ml-metadata/Dockerfile#L15 +[envoy]: https://github.com/opendatahub-io/data-science-pipelines/blob/master/third-party/metadata_envoy/Dockerfile#L15 +[oauth-proxy]: https://catalog.redhat.com/software/containers/openshift4/ose-oauth-proxy/5cdb2133bed8bd5717d5ae64?tag=v4.13.0-202307271338.p0.g44af5a3.assembly.stream&push_date=1691493453000 +[mariaDB]: https://catalog.redhat.com/software/containers/rhel8/mariadb-103/5ba0acf2d70cc57b0d1d9e78 +[ubi-minimal]: https://catalog.redhat.com/software/containers/ubi8/ubi-minimal/5c359a62bed8bd75a2c3fba8?architecture=amd64&tag=8.8 +[ubi-micro]: https://catalog.redhat.com/software/containers/ubi8-micro/601a84aadd19c7786c47c8ea?architecture=amd64&tag=8.8 diff --git a/docs/release/compatibility.yaml b/docs/release/compatibility.yaml new file mode 100644 index 00000000..b774bc56 --- /dev/null +++ b/docs/release/compatibility.yaml @@ -0,0 +1,30 @@ +- dsp: 1.0.x + kfp-tekton: 1.5.1 + ml-metadata: 1.5.0 + envoy: 1.8.4 + ocp-pipelines: v4.10 + oauth-proxy: v4.12 + mariadb-103: 1 + ubi-minimal: 8.8 + ubi-micro: 8.8 + openshift: 4.10,4.11,4.12 +- dsp: 1.1.x + kfp-tekton: 1.5.1 + ml-metadata: 1.5.0 + envoy: 1.8.4 + ocp-pipelines: v4.10 + oauth-proxy: v4.12 + mariadb-103: 1 + ubi-minimal: 8.8 + ubi-micro: 8.8 + openshift: 4.10,4.11,4.12 +- dsp: 1.2.x + kfp-tekton: 1.5.1 + ml-metadata: 1.5.0 + envoy: 1.8.4 + ocp-pipelines: v4.10 + oauth-proxy: v4.10 + mariadb-103: 1 + ubi-minimal: 8.8 + ubi-micro: 8.8 + openshift: 4.10,4.11,4.12 diff --git a/docs/release/release_workflow.md b/docs/release/release_workflow.md new file mode 100644 index 00000000..e235b866 --- /dev/null +++ b/docs/release/release_workflow.md @@ -0,0 +1,97 @@ +# How to create a DSP release + +This doc outlines the steps required for manually preparing and performing a DSP release. + +Versioning for DSP follows [semver]: + +```txt +Given a version number MAJOR.MINOR.PATCH, increment the: + + MAJOR version when you make incompatible API changes + MINOR version when you add functionality in a backward compatible manner + PATCH version when you make backward compatible bug fixes +``` + +DSPO and DSP versioning is tied together, and DSP `MAJOR` versions are tied to [kfp-tekton] upstream. + +> Note: In main branch all images should point to `latest` and not any specific versions, as `main` is rapidly moving, +> it is likely to quickly become incompatible with any specific tags/shas that are hardcoded. + +## Pre-requisites +Need GitHub repo admin permissions for DSPO and DSP repos. + +## Release workflow +Steps required for performing releases for `MAJOR`, `MINOR`, or `PATCH` vary depending on type. + +### MAJOR Releases +Given that `MAJOR` releases often contain large scale, api breaking, changes. It is likely the release process will vary +between each `MAJOR` release. As such, each `MAJOR` release should have a specifically catered strategy. + +### MINOR Releases +Let `x.y.z` be the `latest` release that is highest DSPO/DSP version. + +Steps on how to release `x.y+1.z` + +1. Ensure `compatibility.yaml` is upto date, and generate a new `compatibility.md` + * Use [release-tools] to accomplish this +2. Cut branch `vx.y+1.x` from `main/master`, the trailing `.x` remains unchanged (e.g. `v1.2.x`, `v1.1.x`, etc.) + * Do this for DSPO and DSP repos +3. Build images. Use the [build-tags] workflow +4. Retrieve the sha images from the resulting workflow (check quay.io for the digests) + * Using [release-tools] generate a `params.env` and submit a new pr to vx.y+1.**x** branch + * For images pulled from registry, ensure latest images are upto date +5. Perform any tests on the branch, confirm stability + * If issues are found, they should be corrected in `main/master` and be cherry-picked into this branch. +6. Create a tag release for `x.y+1.z` in DSPO and DSP (e.g. `v1.3.0`) +7. Add any manifest changes to ODH manifests repo using the [ODH sync workflow] + +**Downstream Specifics** + +Downstream maintainers of DSP should forward any manifest changes to their odh-manifests downstream + +### PATCH Releases +DSP supports bug/security fixes for versions that are at most 1 `MINOR` versions behind the latest `MINOR` release. +For example, if `v1.2` is the `latest` DSP release, DSP will backport bugs/security fixes to `v1.1` as `PATCH` (z) releases. + +Let `x.y.z` be the `latest` release that is the highest version.\ +Let `x.y-1.a` be the highest version release that is one `MINOR` version behind `x.y.z` + +**Example**: +If the latest release that is the highest version is `v1.2.0`\ +Then: +```txt +x.y.z = v1.2.0 +x.y-1.a = v1.1.0 +vx.y.z+1 = v1.2.1 +vx.y-1.a+1 = v1.1.1 +``` + +> Note `a` value in `x.y-1.a` is arbitrarily picked here. It is not always the case `z == a`, though it will likely +> be the case most of the time. + +Following along our example, suppose a security bug was found in `main`, `x.y.z`, and `x.y-1.a`. +And suppose that commit `08eb98d` in `main` has resolved this issue. + +Then the commit `08eb98d` needs to trickle to `vx.y.z` and `vx.y-1.a` as `PATCH` (z) releases: `vx.y.z+1` and `vx.y-1.a+1` + +1. Cherry-pick commit `08eb98d` onto relevant minor branches `vx.y.x` and `vx.y-1.x` + * The trailing `.x` in branch names remains unchanged (e.g. `v1.2.x`, `v1.1.x`, etc.) +2. Build images for `vx.y.z+1` and `vx.y-1.a+1` (e.g. `v1.2.1` and `v1.1.1`) DSPO and DSP + * Images should be built off the `vx.y.x` and `vx.y-1.x` branches respectively + * Use the [build-tags] workflow +3. Retrieve the sha image digests from the resulting workflow + * Using [release-tools] generate a params.env and submit a new pr to `vx.y.x` and `vx.y-1.x` branches +4. Cut `vx.y.z+1` and `vx.y-1.a+1` in DSP and DSPO + +**Downstream Specifics** + +Downstream maintainers of DSP should: +* forward any manifest changes to their odh-manifests downstream +* ensure `odh-stable` branches in DSP/DSPO are upto date with bug/security fixes for the appropriate DSPO/DSP versions, + and forward any changes from `odh-stable` to their downstream DSPO/DSP repos + +[semver]: https://semver.org/ +[build-tags]: https://github.com/opendatahub-io/data-science-pipelines-operator/actions/workflows/build-tags.yml +[kfp-tekton]: https://github.com/kubeflow/kfp-tekton +[ODH sync workflow]: https://github.com/opendatahub-io/data-science-pipelines-operator/actions/workflows/odh-manifests-PR-sync.yml +[release-tools]: ../../scripts/release/README.md diff --git a/scripts/release/README.md b/scripts/release/README.md new file mode 100644 index 00000000..869e8073 --- /dev/null +++ b/scripts/release/README.md @@ -0,0 +1,34 @@ +## DSP Release tools + +The scripts found in this folder contain tools utilized for performing a DSP release. + +### Params Generation +This tool will generate a new `params.env` file based on the upcoming DSP tags. + +If images in Red Hat registry have also been updated (e.g. security fixes) without changes to tag version, then the newer +digests will be used. The following command will generate the `params.env`: + +**Pre-condition**: All DSP/DSPO images should have been build with tag +``` +python release.py params --tag v1.2.0 --out_file params.env \ + --override="IMAGES_OAUTHPROXY=registry.redhat.io/openshift4/ose-oauth-proxy@sha256:ab112105ac37352a2a4916a39d6736f5db6ab4c29bad4467de8d613e80e9bb33" +``` + +See `--help` for more options like specifying tags for images not tied to DSP (ubi, mariadb, oauth proxy, etc.) + +### Compatibility Doc generation +Before each release, ensure that the [compatibility doc] is upto date. This doc is auto generated, the version compatibility +is pulled from the [compatibility yaml]. The yaml should be kept upto date by developers (manual). + +To generate the version doc run the following: + +**Pre-condition**: ensure that [compatibility yaml] has an entry for the latest DSP version to be released, with version +compatibility up to date. + +``` +python release.py --input_file compatibility.yaml --out_file compatibility.md +``` + + +[compatibility doc]: ../../docs/release/compatibility.md +[compatibility yaml]: ../../docs/release/compatibility.yaml diff --git a/scripts/release/params.py b/scripts/release/params.py new file mode 100644 index 00000000..7d3f64f9 --- /dev/null +++ b/scripts/release/params.py @@ -0,0 +1,143 @@ +import sys + +import requests + +QUAY_REPOS = { + "IMAGES_APISERVER": "ds-pipelines-api-server", + "IMAGES_ARTIFACT": "ds-pipelines-artifact-manager", + "IMAGES_PERSISTENTAGENT": "ds-pipelines-persistenceagent", + "IMAGES_SCHEDULEDWORKFLOW": "ds-pipelines-scheduledworkflow", + "IMAGES_MLMDENVOY": "ds-pipelines-metadata-envoy", + "IMAGES_MLMDGRPC": "ds-pipelines-metadata-grpc", + "IMAGES_MLMDWRITER": "ds-pipelines-metadata-writer", + "IMAGES_DSPO": "data-science-pipelines-operator", +} + +ARCH = "amd64" + +# RH Registry Env vars +IMAGES_CACHE = "IMAGES_CACHE" +IMAGES_MOVERESULTSIMAGE = "IMAGES_MOVERESULTSIMAGE" +IMAGES_MARIADB = "IMAGES_MARIADB" +IMAGES_OAUTHPROXY = "IMAGES_OAUTHPROXY" + +# RH Registry repos +REPO_UBI_MINIMAL = "ubi8/ubi-minimal" +REPO_UBI_MICRO = "ubi8/ubi-micro" +REPO_MARIADB = "rhel8/mariadb-103" +REPO_OAUTH_PROXY = "openshift4/ose-oauth-proxy" + +# RH Registry servers +RH_REGISTRY_ACCESS = "registry.access.redhat.com" +RH_REGISTRY_IO = "registry.redhat.io" + + +def fetch_quay_repo_tag_digest(quay_repo, quay_org, tag): + api_url = f"https://quay.io/api/v1/repository/{quay_org}/{quay_repo}/tag/?specificTag={tag}" + + response = requests.get(api_url).json() + tags = response['tags'] + + if len(tags) == 0 or 'end_ts' in tags[0]: + print("Tag does not exist or was deleted.", file=sys.stderr) + exit(1) + digest = tags[0].get('manifest_digest') + if not digest: + print("Could not find image digest when retrieving image tag.", file=sys.stderr) + exit(1) + return digest + + +def fetch_rh_repo_tag_digest(repo, tag): + api_url = f"https://catalog.redhat.com/api/containers/v1/repositories/registry/{RH_REGISTRY_ACCESS}/repository/{repo}/tag/{tag}" + + response = requests.get(api_url).json() + + amd_img = {} + for img in response['data']: + arch = img.get('architecture') + if not arch: + print(f"No 'architecture' field found when fetching image from RH registry.", file=sys.stderr) + exit(1) + if img['architecture'] == 'amd64': + amd_img = img + + if not amd_img: + print(f"AMD64 arch image not found for repo {repo} and tag {tag}", file=sys.stderr) + exit(1) + + sha_digest = amd_img['image_id'] + + return sha_digest + + +def generate_params(args): + tag = args.tag + quay_org = args.quay_org + file_out = args.out_file + ubi_minimal_tag = args.ubi_minimal_tag + ubi_micro_tag = args.ubi_micro_tag + mariadb_tag = args.mariadb_tag + oauth_proxy_tag = args.oauth_proxy_tag + + # Structure: { "ENV_VAR": "IMG_DIGEST",...} + overrides = {} + for override in args.overrides: + entry = override.split('=') + if len(entry) != 2: + print("--override values must be of the form var=digest,\n" + "e.g: IMAGES_OAUTHPROXY=registry.redhat.io/openshift4/ose-oauth-proxy" + "@sha256:ab112105ac37352a2a4916a39d6736f5db6ab4c29bad4467de8d613e80e9bb33", file=sys.stderr) + exit(1) + overrides[entry[0]] = entry[1] + + images = [] + # Fetch QUAY Images + for image_env_var in QUAY_REPOS: + if image_env_var in overrides: + images.append(f"{image_env_var}={overrides[image_env_var]}") + else: + image_repo = QUAY_REPOS[image_env_var] + digest = fetch_quay_repo_tag_digest(image_repo, quay_org, tag) + image_repo_with_digest = f"{image_repo}@{digest}" + images.append(f"{image_env_var}=quay.io/opendatahub/{image_repo_with_digest}") + + # Fetch RH Registry images + rh_registry_images = { + RH_REGISTRY_IO: [ + { + "repo": REPO_UBI_MINIMAL, + "tag": ubi_minimal_tag, + "env": IMAGES_CACHE + }, + { + "repo": REPO_UBI_MICRO, + "tag": ubi_micro_tag, + "env": IMAGES_MOVERESULTSIMAGE + }, + ], + RH_REGISTRY_ACCESS: [ + { + "repo": REPO_MARIADB, + "tag": mariadb_tag, + "env": IMAGES_MARIADB + }, + { + "repo": REPO_OAUTH_PROXY, + "tag": oauth_proxy_tag, + "env": IMAGES_OAUTHPROXY + }, + ] + } + for registry in rh_registry_images: + for img in rh_registry_images[registry]: + image_env_var, tag, repo = img['env'], img['tag'], img['repo'] + if image_env_var in overrides: + images.append(f"{image_env_var}={overrides[image_env_var]}") + else: + digest = fetch_rh_repo_tag_digest(repo, tag) + images.append(f"{image_env_var}={registry}/{repo}@{digest}") + + with open(file_out, 'w') as f: + for images in images: + f.write(f"{images}\n") diff --git a/scripts/release/release.py b/scripts/release/release.py new file mode 100755 index 00000000..5f82a098 --- /dev/null +++ b/scripts/release/release.py @@ -0,0 +1,48 @@ +import argparse + +from params import generate_params +from version_doc import version_doc + + +def main(): + parser = argparse.ArgumentParser( + description="DSP Release Tools." + ) + + subparsers = parser.add_subparsers(help='sub-command help', required=True) + + # Params.env generator inputs + parser_params = subparsers.add_parser('params', help='Params.env generator inputs') + parser_params.set_defaults(func=generate_params) + parser_params.add_argument('--tag', type=str, required=True, help='Tag for which to fetch image digests for.') + parser_params.add_argument('--quay_org', default="opendatahub", type=str, + help='Tag for which to fetch image digests for.') + parser_params.add_argument('--out_file', default='params.env', type=str, help='File path output for params.env') + parser_params.add_argument("--ubi-minimal", dest="ubi_minimal_tag", default="8.8", + help="ubi-minimal version tag in rh registry") + parser_params.add_argument("--ubi-micro", dest="ubi_micro_tag", default="8.8", + help="ubi-micro version tag in rh registry") + parser_params.add_argument("--mariadb", dest="mariadb_tag", default="1", + help="mariadb version tag in rh registry") + parser_params.add_argument("--oauthproxy", dest="oauth_proxy_tag", default="v4.10", + help="oauthproxy version tag in rh registry") + + parser_params.add_argument("--override", dest="overrides", + help="Override an env var with a manually submitted digest " + "entry of the form --overide=\"ENV_VAR=DIGEST\". Can be " + "used for multiple entries by using --override multiple times.", + action='append') + + # Version Compatibility Matrix doc generator + parser_vd = subparsers.add_parser('version_doc', help='Version Compatibility Matrix doc generator') + parser_vd.set_defaults(func=version_doc) + parser_vd.add_argument('--out_file', default='compatibility.md', type=str, help='File output for markdown doc.') + parser_vd.add_argument('--input_file', default='compatibility.yaml', type=str, + help='Yaml input for compatibility doc generation.') + + args = parser.parse_args() + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/scripts/release/template/version_doc.md b/scripts/release/template/version_doc.md new file mode 100644 index 00000000..274d948c --- /dev/null +++ b/scripts/release/template/version_doc.md @@ -0,0 +1,30 @@ +# DSP Version Compatibility Table + +This is an auto generated DSP version compatibility table. +Each row outlines the versions for individual subcomponents and images that are leveraged within DSP. + +For some components, the versions match with their respective image tags within their respective Quay, GCR, or RedHat image +registries, this is true for the following: + +* [ml-metadata] +* [envoy] +* [oauth-proxy] + * for Oauth Proxy DSP follows the same version digest as the Oauth Proxy leveraged within the rest of ODH. +* [mariaDB] + * for MariaDB the entire column represents different tag versions for MariDB Version 10.3, DSP follows the latest digest for the `1` tag + for each DSP release. +* [ubi-minimal] + * Used for default base images during Pipeline Runs +* [ubi-micro] + * Used for default cache image for runs + + +<> + + +[ml-metadata]: https://github.com/opendatahub-io/data-science-pipelines/blob/master/third-party/ml-metadata/Dockerfile#L15 +[envoy]: https://github.com/opendatahub-io/data-science-pipelines/blob/master/third-party/metadata_envoy/Dockerfile#L15 +[oauth-proxy]: https://catalog.redhat.com/software/containers/openshift4/ose-oauth-proxy/5cdb2133bed8bd5717d5ae64?tag=v4.13.0-202307271338.p0.g44af5a3.assembly.stream&push_date=1691493453000 +[mariaDB]: https://catalog.redhat.com/software/containers/rhel8/mariadb-103/5ba0acf2d70cc57b0d1d9e78 +[ubi-minimal]: https://catalog.redhat.com/software/containers/ubi8/ubi-minimal/5c359a62bed8bd75a2c3fba8?architecture=amd64&tag=8.8 +[ubi-micro]: https://catalog.redhat.com/software/containers/ubi8-micro/601a84aadd19c7786c47c8ea?architecture=amd64&tag=8.8 diff --git a/scripts/release/version_doc.py b/scripts/release/version_doc.py new file mode 100644 index 00000000..2fe13437 --- /dev/null +++ b/scripts/release/version_doc.py @@ -0,0 +1,49 @@ +import yaml + + +def table(rows): + """ + Convert a list of cits into a markdown table. + + Pre-condition: All dicts in list_of_dicts should have identical key_sets + :param rows: list of dict where each key set for every dict matches list of cols + :return: A markdown where each row corresponds to a dict in list_of_dicts + """ + + markdown_table = "" + if len(rows) == 0: + return markdown_table + + cols = [] + for row in rows: + cols.extend([key for key in row.keys() if key not in cols]) + + markdown_header = '| ' + ' | '.join(cols) + ' |' + markdown_header_separator = '|-----' * len(cols) + '|' + markdown_table += markdown_header + '\n' + markdown_table += markdown_header_separator + '\n' + for row in rows: + markdown_row = "" + for col in cols: + markdown_row += '| ' + str(row[col]) + ' ' + markdown_row += '|' + '\n' + markdown_table += markdown_row + return markdown_table + + +def version_doc(args): + input_file = args.input_file + out_file = args.out_file + with open(input_file, 'r') as f: + rows = yaml.safe_load(f) + + with open('template/version_doc.md', 'r') as vd: + final_md = vd.read() + + table_md = table(rows) + + final_md = final_md.replace('<>', table_md) + final_md = '\n' + final_md + + with open(out_file, 'w') as f: + f.write(final_md)