From 8e23d47cd63aaa4382c70bdaf545f16431d0cea5 Mon Sep 17 00:00:00 2001 From: Chloe Yip Date: Thu, 3 Oct 2024 14:18:46 -0700 Subject: [PATCH] addressed all comments -> split file into 2 sections Signed-off-by: Chloe Yip --- RELEASE.md | 256 ------------------ RELEASING.md | 208 ++++++++++++++ .../images}/actions-secrets-variables.png | Bin .../images}/new-access-token.png | Bin docs/images/new-branch.png | Bin 0 -> 14061 bytes .../images}/oss-review-toolkit.png | Bin .../images/releasing}/pypi-CD.png | Bin images-release-md/valkey-version.png | Bin 10582 -> 0 bytes 8 files changed, 208 insertions(+), 256 deletions(-) delete mode 100644 RELEASE.md create mode 100644 RELEASING.md rename {images-release-md => docs/images}/actions-secrets-variables.png (100%) rename {images-release-md => docs/images}/new-access-token.png (100%) create mode 100644 docs/images/new-branch.png rename {images-release-md => docs/images}/oss-review-toolkit.png (100%) rename {images-release-md => docs/images/releasing}/pypi-CD.png (100%) delete mode 100644 images-release-md/valkey-version.png diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index 4f7a591524..0000000000 --- a/RELEASE.md +++ /dev/null @@ -1,256 +0,0 @@ -# Release Documentation - -This document describes the process on how to release a new version of Valkey-GLIDE, how to use the GLIDE's CD workflows, and how to release a new wrapper. - -## GLIDE - How to release a new version - -**Contact a Valkey-GLIDE maintainer** on how to trigger the CD workflows and on how to login to the package manager accounts and how to access the relevant secrets. - -## Deployment Guide - -1. CHANGELOG: go through the CHANGELOG.md file found in the root folder and verify all required changes / bug fixes / features for this release are found in the file. Add a headline of the version and the release date to close all of its changes, for example for releasing version 0.3.0: - - ### 0.3.0 (2024-03-25) - #### Changes - * Python: Allow routing Cluster requests by address. ([#1021](https://github.com/aws/glide-for-redis/pull/1021)) - * Python, Node: Added HSETNX command. ([#954](https://github.com/aws/glide-for-redis/pull/954), [#1091](https://github.com/aws/glide-for-redis/pull/1091)) - - #### Features - * Python: Allow chaining function calls on transaction. ([#987](https://github.com/aws/glide-for-redis/pull/987)) - - ### 0.2.0 (2024-02-11) - #### Changes - * Python, Node: Added ZCARD command ([#871](https://github.com/aws/glide-for-redis/pull/871), [#885](https://github.com/aws/glide-for-redis/pull/885)) - - b. Open a PR to the main branch with the missing changes and the new version header, and continue after it’s merged. - - -2. **If it’s a new minor/major version**, create a new branch named in the format `v$MAJOR_VERSION.$MINOR_VERSION` (e.g., v0.2), and checkout only the relevant code for this release (probably the latest commit in main, including the CHANGELOG latest commit).
- - MAJOR_VERSION = 0
- MINOR_VERSION = 3
- git checkout -b `v$MAJOR_VERSION.$MINOR_VERSION`
- - a. Find how you saved the upstream remote repo (valkey-io/valkey-glide): - ``` - [10:09:23] ubuntu (v1.x*)$ git remote -vv - origin https://github.com/your-repo/valkey-glide (fetch) - origin https://github.com/your-repo/valkey-glide (push) - origin-aws https://github.com/valkey-io/valkey-glide (fetch) - origin-aws https://github.com/valkey-io/valkey-glide (push) - ``` - - b. Push this branch to the upstream repo: - git push origin-aws - - 1. **If it’s only a patch version and the branch already exists**, checkout the existing branch (e.g. git checkout origin-aws/v0.2), and merge in the commit you’ve merge into main with the updates to the CHANGELOG.md file. - 2. **Attributions file (ORT)**: Make sure the attributions file is updated by manually running the ORT workflow: - - a. Go to: https://github.com/valkey-io/valkey-glide/actions/workflows/ort.yml
- - b. On the right corner, click on “Run workflow” and set the branch to run against the ORT tool to your newly created branch name (e.g. v0.3), and the exact release version (e.g. 0.3.0)
- - c.
- ![OSS Review Toolkit](images-release-md/oss-review-toolkit.png) - - d. If diff is found the workflow will open a PR in the release branch with the required changes. Make sure that all dependencies are approved by a Valkey-GLIDE maintainer, refer to the SOP - Check the Licenses: ORT/Attributions File Setup and Usage Instructions. Merge the PR once it is ready. - - 1. Trigger the CD workflow - - a. The CD workflow will deploy the code into the package managers of the different wrappers. If there is any issue with the CD, **contact a Valkey-GLIDE maintainer**. - - b. To trigger a new release we need to tag the new branch commit with the release version, if its a release candidate, which should - be release before a real release in order to test the version, add - -rc$RC_VERSION: - - c. git tag v1.x.x-rc0 - - d. Find how you saved the upstream remote repo (valkey-io/valkey-glide): - - e. - - ``` - [10:09:23] ubuntu (v1.x*)$ git remote -vv - origin https://github.com/your-repo/valkey-glide (fetch) - origin https://github.com/your-repo/valkey-glide (push) - origin-aws https://github.com/valkey-io/valkey-glide (fetch) - origin-aws https://github.com/valkey-io/valkey-glide (push) - ``` - - f. Push the tag to the upstream remote so it would trigger the CD action: - - g. git push origin-aws v1.x.x-rc0 - - h. For each time your release fails all you need to do is create new tag in which the next rc version (e.g. v0.5.0-rc3 → v0.5.0-rc4). - - i. After the release candidate deployed, in node tests running automatically and when release candidate CD workflow is done successfully we can immediately release the real version. In python there’s no such support currently, so you'll first need to test the RC version you deployed on the different supported platform.You can do so by cloning the repo into each machine, go to [examples/python/requirements.txt](https://github.com/valkey-io/valkey-glide/blob/main/examples/python/requirements.txt). - - j. and change the version to the version you just released. - - ![Valkey version](images-release-md/valkey-version.png) - - k. From the root directory, - run `cd examples/python` - l. Then, - run `pip install -r requirements.txt` - m. Then, - run `pip list` and validate that the right version has been installed. - n. run `python3 ../../utils/cluster_manager.py -r 1 -n 3 --cluster-mode` - and - `python3 ../../utils/cluster_manager.py -r 1 -n 1` - - o. Edit the file [standalone_example.py](https://github.com/valkey-io/valkey-glide/blob/main/examples/python/standalone_example.py) or [cluster_example.py](https://github.com/valkey-io/valkey-glide/blob/main/examples/python/standalone_example.py) to have the right port in the different functions and run python3. - p. Once the CD workflow and all test have passed, you can release a real version such as `git tag v1.3.0`. - - q. Find how you saved the upstream remote repo (valkey-io/valkey-glide): - - r. - ``` - [10:09:23] ubuntu (v1.x*)$ git remote -vv - origin https://github.com/your-repo/valkey-glide (fetch) - origin https://github.com/your-repo/valkey-glide (push) - origin-aws https://github.com/valkey-io/valkey-glide (fetch) - origin-aws https://github.com/valkey-io/valkey-glide (push) - ``` - s. Push the tag to the upstream remote so it will trigger the CD action: - - t. git push origin-aws v1.x.x - - -2. Update the release notes in github - - a. Go to: https://github.com/valkey-io/valkey-glide/releases - - b. Click on "Draft a new release" - - c. Add the release tag - - d. Copy + paste the release notes from the CHANGELOG.md file - -## How to use GLIDE's CD workflows - -For deployment guide, see the above section **`GLIDE - How to release a new version`**. - -### **Secrets** - -Please **contact a Valkey-GLIDE maintainer** to access the secrets for this repository. - -### **How to trigger the CD workflow** - -The CD workflow can be triggered in several ways. - -1. **By pushing a Version Tag**: Trigger the CD workflow by pushing a version tag in the format "v*.*." or "v.*.*-rc". -For example, v1.0.0-rc1. When triggered by a push event with a version tag, all package manager CD workflows (e.g., pypi-cd.yml, npm-cd.yml, java-cd.yml) will be initiated. -The version will be extracted from the tag. - - a. - ``` - [13:39:23] ubuntu $ git remote -vv - origin https://github.com/your-repo/valkey-glide (fetch) - origin https://github.com/your-repo/valkey-glide (push) - origin-aws https://github.com/valkey-io/valkey-glide (fetch) - origin-aws https://github.com/valkey-io/valkey-glide (push) - ``` - - -2. **By Workflow Dispatch:** Manually trigger the CD workflow using the "workflow dispatch" option. -This is done on a specific CD workflow by passing the required version as an input parameter in the format "*.*.*" or "*.*.*-rc*". -This method will not trigger deployments for other package managers and will not create a tag in the repository. - - a.
- ![pypi CD](images-release-md/pypi-CD.png) - - -3. **Pull Requests:** Pull requests will trigger the CD workflows as "dry runs" without actually publishing to the package managers. -This ensures that changes do not break the CD logic. Note that PRs affecting the CD workflows (e.g., introducing changes in the CD paths) can only be opened from branches on the -valkey-io/valkey-glide main repo (rather than from forks) since running the CD, even without publishing, requires access to the repository's secrets. - - -### **Self-hosted runner** - -We use a self-hosted runner for Linux-arm64, as GitHub does not currently provide one. -We have an automated action to start the self-hosted runner, detailed here: [automatic action to start the self-hosted runner.](https://github.com/valkey-io/valkey-glide/pull/1128) - -To manually start the self-hosted runner or to log into it for debugging purposes, **contact a Valkey-GLIDE maintainer**. - -### **PyPi** - -**Contact a Valkey-GLIDE maintainer** to have access to the AWS PyPI account-they will be able to log ing to the PyPI account along with generating API tokens if needed. - -### **NPM** - -**Contact a Valkey-GLIDE maintainer** to create a Valkey-GLIDE npm account. - -1. Create an account. - - a. Once you have created your [NPM account](https://www.npmjs.com/signup). Ensure that you have 2FA enabled. - Then use npm login in your shell to sign in with it. Do not share a team account. You should generally be using an account unique to you. - - b. If you need a bot user to perform build actions, **contact a Valkey-GLIDE maintainer**. - - c. When you're ready to make your first release (including checking that things are OK with npm pack), you can run npm publish. - Your artifacts will be uploaded to the registry, and your package will be live. You can view it at https://npmjs.com/package/package-name-here. - - d. Once your package is created, you'll need to do some set-up online to ensure it's properly linked to company resources. Add the user "amzn-oss" to your package's owner list. - This helps out with a couple of things: - - i. If your somehow lose access to your own account, we can help recover your package.
- ii. If everyone loses access, the "amzn-oss" account is on record to prove ownership. - - e. We used to have instructions here to have you "register" your package with the aws/amzn/etc orgs. - We no longer recommend this as it requires manual action with npm support for very little operational gain. - -
-2. Create an access tokens - -a. Access Tokens → Generate new token → classic token - -b. Use a separate [granular access token](https://docs.npmjs.com/about-access-tokens#about-granular-access-tokens) for every actor on every project. -For example, if you have CD automation publishing to npm on two projects, you should have two separate granular tokens, each of which only has permissions on a single project. -This reduces impact when you rotate these tokens and reduces risk if there is ever a security issue with a token. - -c. **Use an Automation token so it could be used in CD workflow:** -An automation token will bypass two-factor authentication (2FA) when publishing. -If you have 2FA enabled, you will not be prompted when using an automation token, making it suitable for CI/CD workflows. - -![new access token](images-release-md/new-access-token.png) - -3. Change the NPM_AUTH_TOKEN secret in github with the new access token. - -4. Change the NPM_SCOPE variable to an empty string. - -![actions secrets ss](images-release-md/actions-secrets-variables.png) - -5. **To test publishing in a private mode,** keep the current NPM access token and scope, it will be uploaded to private account-**contact a Valkey-GLIDE maintainer** about accessing the token. - - -### **Maven Central** - -**Contact a Valkey-GLIDE maintainer** about using a self-hosted runner. - - -## **GLIDE - How to release a new wrapper** - -TODO list: - -1. Create a README.md file following the python [README](python/README.md) - -2. Create a DEVELOPER.md file following the python [DEVELOPER.md](python/DEVELOPER.md) - -3. Create examples following the python [examples](https://github.com/valkey-io/valkey-glide/tree/main/examples/python) - -4. Update the main [README](README.md) file with link to the new wrapper readme under Getting Started - -5. Update the Current Status in the main README file - -6. Make sure all required structures/types/enums are exported - -7. Create a workflow to automate code deployment to the relevant package manager (see pypi_cd.yml for example) - -8. Add this wrapper to the ort.yml workflow so it would generate contributions file for this wrapper, make sure that the folder has updated THIRD_PARTY_LICENSES_ file. **Contact a Valkey-GLIDE maintainer** for more details. - -9. Check that there is no vulnerabilities - -10. Contact a Valkey-Glide maintainer for access to the GLIDE - How to release a new version document to release the new version to the package managers. - -11. Create a new brazil package for this wrapper diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000000..8e200554dd --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,208 @@ +# Release Documentation + +This document describes the process on how to release a new version of Valkey-GLIDE, how to use the GLIDE's CD workflows, and how to release a new wrapper. + +## GLIDE - How to release a new version + +**Contact a Valkey-GLIDE maintainer** on how to trigger the CD workflows. + +## Deployment Guide + +1. CHANGELOG: Go through the CHANGELOG.md file found in the root folder and verify all required changes / bug fixes / features for this release are found in the file. Add a header with of the version and the release date above the described changes. For example for releasing version 0.3.0: + +``` +1.0.0 (2024-07-09) +Changes +* Node: Added ZINTERSTORE command ([#1513](https://github.com/valkey-io/valkey-glide/pull/1513)) +* Python: Added OBJECT ENCODING command ([#1471](https://github.com/valkey-io/valkey-glide/pull/1471)) + +0.4.0 (2024-05-26) +Changes +* Python: Added STRLEN command ([#1230](https://github.com/valkey-io/valkey-glide/pull/1230)) +``` + +Open a PR to the main branch with the missing changes and the new version header, and continue after it’s merged. + + +2. **Ask a maintainer** to create a new branch named v$A.$B which can be made through the Github UI or on their local device. + + ![new branch](docs/images/new-branch.png) + + +1. **If it’s only a patch version and the branch already exists**, checkout the existing branch (e.g. git checkout origin-valkey/v0.2), and merge in the commit you’ve merged into main with the updates to the CHANGELOG.md file. +2. **Attributions file (ORT)**: Make sure the attributions file is updated by manually running the ORT workflow: + + a. Go to: https://github.com/valkey-io/valkey-glide/actions/workflows/ort.yml
+ + b. On the right corner, click on "Run workflow" and set the branch to run against the ORT tool to your newly created branch name (e.g. v0.3), and the exact release version (e.g. 0.3.0)
+ + c.
+ ![OSS Review Toolkit](docs/images/oss-review-toolkit.png) + + d. If diff is found the workflow will open a PR in the release branch with the required changes. Make sure that all dependencies are approved by a Valkey-GLIDE maintainer. Merge the PR once it is ready. + +### Trigger the CD workflow for Release + +a. The CD workflow will deploy the code into the package managers of the different wrappers. If there is any issue with the CD, **contact a Valkey-GLIDE maintainer**. There are multiple CD jobs, specifically one per client. All [CD files](https://github.com/valkey-io/valkey-glide/tree/main/.github/workflows) can be found in the workflows section. Refer to Java CD as an [example](https://github.com/valkey-io/valkey-glide/blob/main/.github/workflows/java-cd.yml).
+ +b. To trigger a new release we need to tag the new branch commit with the release version, If it's a release candidate, which should + be released before a real release in order to test the version, add + `-rc$RC_VERSION:` + +c. `git tag v1.x.x-rc0` + +d. Push the tag to the valkey/glide repo to trigger the CD action + +e. `git push origin-valkey v1.x.x-rc0` + +f. For each time your release fails all you need to do is create new tag in which the next rc version (e.g. v0.5.0-rc3 → v0.5.0-rc4). + +g. +There is a different procedure for each client once the release candidate is deployed. + +**Node:** all the test will automatically run and once the release candidate CD workflow is done successfully, we can immediately release the final version. + +**Python:** there’s no such support currently, so you'll first need to test the RC version you deployed on the different supported platform.You can do so by cloning the repo into each machine, go to [examples/python/requirements.txt](https://github.com/valkey-io/valkey-glide/blob/main/examples/python/requirements.txt). + + +h. and change the version to the version you just released. + +``` +valkey-glide==v0.5.0rc3 +``` + +For example, this is the release process for Python. + +i. From the root directory, + run `cd examples/python` + +j. Then, + run `pip install -r requirements.txt` + +k. Then, + run `pip list` and validate that the right version has been installed. + +l. run `python3 ../../utils/cluster_manager.py -r 1 -n 3 --cluster-mode` + and + `python3 ../../utils/cluster_manager.py -r 1 -n 1` + +m. Edit the file [standalone_example.py](https://github.com/valkey-io/valkey-glide/blob/main/examples/python/standalone_example.py) or [cluster_example.py](https://github.com/valkey-io/valkey-glide/blob/main/examples/python/standalone_example.py) to have the right port in the different functions and run python3. + +n. Once the CD workflow and all test have passed, you can release a real version such as `git tag v1.3.0`. + +o. Push the tag to the valkey/glide repo so it will trigger the CD action: + +p. `git push origin-valkey v1.x.x` + + +2. Update the release notes in Github + +a. Go to: https://github.com/valkey-io/valkey-glide/releases + +b. Click on "Draft a new release" + +## How to use GLIDE's CD workflows + +For deployment guide, see the above section **`GLIDE - How to release a new version`**. + +### **How to trigger the CD workflow for Development** + +The CD workflow can be triggered in several ways. + +1. **By pushing a Version Tag**: Trigger the CD workflow by pushing a version tag in the format "v*.*." or "v.*.*-rc". +For example, v1.0.0-rc1. When triggered by a push event with a version tag, all package manager CD workflows (e.g., pypi-cd.yml, npm-cd.yml, java-cd.yml) will be initiated. +The version will be extracted from the tag. + + a. + ``` + [13:39:23] ubuntu $ git remote -vv + origin https://github.com/your-repo/valkey-glide (fetch) + origin https://github.com/your-repo/valkey-glide (push) + origin-valkey https://github.com/valkey-io/valkey-glide (fetch) + origin-valkey https://github.com/valkey-io/valkey-glide (push) + ``` + + +2. **By Workflow Dispatch:** Manually trigger the CD workflow using the "workflow dispatch" option. +This is done on a specific CD workflow by passing the required version as an input parameter in the format "*.*.*" or "*.*.*-rc*". +This method will not trigger deployments for other package managers and will not create a tag in the repository. + + a.
+ ![pypi CD](docs/images/releasing/pypi-CD.png) + + +3. **Pull Requests:** Pull requests will trigger the CD workflows as "dry runs" without actually publishing to the package managers. +This ensures that changes do not break the CD logic. Note that PRs affecting the CD workflows (e.g., introducing changes in the CD paths) can only be opened from branches on the +valkey-io/valkey-glide main repo (rather than from forks) since running the CD, even without publishing, requires access to the repository's secrets. + + +### **Self-hosted runner** + +We use a self-hosted runner for Linux-arm64. +We have an automated action to start the self-hosted runner, detailed here: [automatic action to start the self-hosted runner.](https://github.com/valkey-io/valkey-glide/pull/1128) + +### **PyPi** + +**Contact a Valkey-GLIDE maintainer** to have access to the Valkey PyPI account-they will be able to log ing to the PyPI account along with generating API tokens if needed. + +### **NPM** + +**Contact a Valkey-GLIDE maintainer** to create a Valkey-GLIDE npm account. + +#### 1. Create an account. + +a. Once you have created your [NPM account](https://www.npmjs.com/signup). Ensure that you have 2FA enabled. +Then use npm login in your shell to sign in with it. Do not share a team account. You should generally be using an account unique to you. + +b. When you're ready to make your first release (including checking that things are OK with npm pack), you can run npm publish. +Your artifacts will be uploaded to the registry, and your package will be live. You can view it at https://npmjs.com/package/package-name-here. + +### 2. Create an access tokens + +a. Access Tokens → Generate new token → classic token + +b. Use a separate [granular access token](https://docs.npmjs.com/about-access-tokens#about-granular-access-tokens) for every actor on every project. +For example, if you have CD automation publishing to npm on two projects, you should have two separate granular tokens, each of which only has permissions on a single project. +This reduces impact when you rotate these tokens and reduces risk if there is ever a security issue with a token. + +c. **Use an Automation token so it could be used in CD workflow:** + +An automation token will bypass two-factor authentication (2FA) when publishing. +If you have 2FA enabled, you will not be prompted when using an automation token, making it suitable for CI/CD workflows. + +![new access token](docs/images/new-access-token.png) + +3. Change the NPM_AUTH_TOKEN secret in Github with the new access token. + +4. Change the NPM_SCOPE variable to an empty string. + +![actions secrets ss](docs/images/actions-secrets-variables.png) + +### **Maven Central** + +**Contact a Valkey-GLIDE maintainer** about using a self-hosted runner. + + +## **GLIDE - How to release a new wrapper** + +TODO list: + +- [ ] Create a README.md file following the python [README](python/README.md) + +- [ ] Create a DEVELOPER.md file following the python [DEVELOPER.md](python/DEVELOPER.md) + +- [ ] Create examples following existing client [examples](https://github.com/valkey-io/valkey-glide/tree/main/examples)-[python](https://github.com/valkey-io/valkey-glide/tree/main/examples/python), [java](https://github.com/valkey-io/valkey-glide/tree/main/examples/java), and [node](https://github.com/valkey-io/valkey-glide/tree/main/examples/node). + +- [ ] Update the main [README](README.md) file with link to the new wrapper readme under Getting Started + +- [ ] Update the Current Status in the main README file + +- [ ] Make sure all required structures/types/enums are exported + +- [ ] Create a workflow to automate code deployment to the relevant package manager (see pypi_cd.yml for example) + +- [ ] Add this wrapper to the ort.yml workflow so it would generate contributions file for this wrapper, make sure that the folder has updated THIRD_PARTY_LICENSES_ file. + +- [ ] Check that there is no vulnerabilities + +- [ ] Contact a Valkey-Glide maintainer for access to the GLIDE - How to release a new version document to release the new version to the package managers. diff --git a/images-release-md/actions-secrets-variables.png b/docs/images/actions-secrets-variables.png similarity index 100% rename from images-release-md/actions-secrets-variables.png rename to docs/images/actions-secrets-variables.png diff --git a/images-release-md/new-access-token.png b/docs/images/new-access-token.png similarity index 100% rename from images-release-md/new-access-token.png rename to docs/images/new-access-token.png diff --git a/docs/images/new-branch.png b/docs/images/new-branch.png new file mode 100644 index 0000000000000000000000000000000000000000..0a711e7f0cc800f116e442008962b333d335e488 GIT binary patch literal 14061 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 E8%&y45dZ)H literal 0 HcmV?d00001 diff --git a/images-release-md/oss-review-toolkit.png b/docs/images/oss-review-toolkit.png similarity index 100% rename from images-release-md/oss-review-toolkit.png rename to docs/images/oss-review-toolkit.png diff --git a/images-release-md/pypi-CD.png b/docs/images/releasing/pypi-CD.png similarity index 100% rename from images-release-md/pypi-CD.png rename to docs/images/releasing/pypi-CD.png diff --git a/images-release-md/valkey-version.png b/images-release-md/valkey-version.png deleted file mode 100644 index 575d325d0ddddf04e7973a6b033ac8e2034645b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10582 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