From c5bf2fb3c13ae62c4e53d21cee0f31682438630f Mon Sep 17 00:00:00 2001 From: Tapish Rathore Date: Sat, 16 Sep 2023 15:54:31 +0530 Subject: [PATCH 01/11] add github related cicd from dev-profiler repo --- .github/pull_request_template.md | 6 +++ .github/workflows/release.yml | 81 ++++++++++++++++++++++++++++++++ .github/workflows/rust.yml | 26 ++++++++++ scripts/update.py | 23 +++++++++ 4 files changed, 136 insertions(+) create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/rust.yml create mode 100644 scripts/update.py diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..a1ba9e1c --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,6 @@ +Please check the action items covered in the PR - + +- [ ] Build is running +- [ ] Eventing is functional and tested +- [ ] Unit or integration tests added and running +- [ ] Manual QA \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..0f2f8875 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,81 @@ + +name: Auto-version + +on: + pull_request: + types: + - closed + +jobs: + build: + if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main' && github.event.pull_request.head.ref != 'update-cargo-toml' + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Run Build Script + run: | + git config user.name "GitHub Actions Bot" + git config user.email "<>" + + - name: run script + run: | + python scripts/update.py + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "Updated code" + title: "Update Cargo.toml" + body: "This pull request updates the Cargo.toml file." + branch: update-cargo-toml + reviewers: tapishr + + - name: Build Rust CLI + run: | + cd vibi-dpu + cargo build --release + + - name: Get version from Cargo.toml + id: get_version + run: | + cd vibi-dpu + echo "::set-output name=version::$(grep -Po '(?<=version = ")[\d.]+(?=")' Cargo.toml)" + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.get_version.outputs.version }} + release_name: Release ${{ steps.get_version.outputs.version }} + draft: false + prerelease: false + + - name: cargo deb + run: | + cargo install cargo-deb + + - name: build binary + run: | + cd vibi-dpu + cargo deb + + - name: Rename Debian Package + run: mv ./vibi-dpu/target/debian/*.deb ./vibi-dpu/target/debian/vibi-dpu.deb + + - name: Upload .deb Package + id: upload_deb + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./vibi-dpu/target/debian/vibi-dpu.deb + asset_name: vibi-dpu.deb + asset_content_type: application/octet-stream + diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 00000000..476e9c60 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,26 @@ +name: Rust + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Build + run: | + cd vibi-dpu + cargo build --verbose + - name: Run tests + run: | + cd vibi-dpu + cargo test --verbose diff --git a/scripts/update.py b/scripts/update.py new file mode 100644 index 00000000..c9c38ede --- /dev/null +++ b/scripts/update.py @@ -0,0 +1,23 @@ + +import re +import fileinput + +# Read the current version from the Cargo.toml file +current_version = "" +with open("devprofiler/Cargo.toml", "r") as cargo_file: + for line in cargo_file: + match = re.search(r'^version\s*=\s*"(.*?)"', line) + if match: + current_version = match.group(1) + break + +# Generate a new version number (increment the patch version) +version_parts = current_version.split('.') +new_patch = int(version_parts[2]) + 1 +new_version = f"{version_parts[0]}.{version_parts[1]}.{new_patch}" + +# Update the Cargo.toml file with the new version number +for line in fileinput.input("devprofiler/Cargo.toml", inplace=True): + line = re.sub(r'^version\s*=\s*".*?"', f'version = "{new_version}"', line.rstrip()) + print(line) + From 2c47865473996592bc698d671da0c9a480e32698 Mon Sep 17 00:00:00 2001 From: Tapish Rathore Date: Sat, 16 Sep 2023 16:01:39 +0530 Subject: [PATCH 02/11] fix version change in python script --- scripts/update.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/update.py b/scripts/update.py index c9c38ede..58a1dd00 100644 --- a/scripts/update.py +++ b/scripts/update.py @@ -17,7 +17,14 @@ new_version = f"{version_parts[0]}.{version_parts[1]}.{new_patch}" # Update the Cargo.toml file with the new version number +in_package_section = False for line in fileinput.input("devprofiler/Cargo.toml", inplace=True): - line = re.sub(r'^version\s*=\s*".*?"', f'version = "{new_version}"', line.rstrip()) + if line.strip() == "[package]": + in_package_section = True + elif line.strip().startswith("["): + in_package_section = False + + if in_package_section: + line = re.sub(r'^version\s*=\s*".*?"', f'version = "{new_version}"', line.rstrip()) print(line) From b4100a01761511d7be4375d2f388f7193923bd3a Mon Sep 17 00:00:00 2001 From: Tapish Rathore Date: Sat, 16 Sep 2023 16:06:37 +0530 Subject: [PATCH 03/11] revert changes of previous commits since not working --- scripts/update.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/scripts/update.py b/scripts/update.py index 58a1dd00..c9c38ede 100644 --- a/scripts/update.py +++ b/scripts/update.py @@ -17,14 +17,7 @@ new_version = f"{version_parts[0]}.{version_parts[1]}.{new_patch}" # Update the Cargo.toml file with the new version number -in_package_section = False for line in fileinput.input("devprofiler/Cargo.toml", inplace=True): - if line.strip() == "[package]": - in_package_section = True - elif line.strip().startswith("["): - in_package_section = False - - if in_package_section: - line = re.sub(r'^version\s*=\s*".*?"', f'version = "{new_version}"', line.rstrip()) + line = re.sub(r'^version\s*=\s*".*?"', f'version = "{new_version}"', line.rstrip()) print(line) From 855202d8c199efbc2de09dcc2a2db17d4ed39389 Mon Sep 17 00:00:00 2001 From: Tapish Rathore Date: Wed, 20 Sep 2023 15:17:08 +0530 Subject: [PATCH 04/11] Add setup instructions and description to Readme --- README.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 922405e8..23abdc5d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,47 @@ -# vibi-dpu -Vibinex Data Processing Unit for collecting and analyzing private data +# Vibi-DPU + +Vibi-DPU is an application written in Rust and packaged as a Docker image. It runs on the users' infrastructure to analyze private Intellectual Property data, empowering users through analysis without sacrificing privacy. + +The application communicates with our Next.js server, hosted on our infrastructure, which is also open source. You can find it [here](https://github.com/Alokit-Innovations/team-monitor-website/). + +Currently, we analyze code in Git repositories, and are soon planning to add APM data and business events. The insights we get from these analyses are communicated through comments/actions on pull requests and through our [open-source Chrome Extension](https://chrome.google.com/webstore/detail/vibinex-code-review/jafgelpkkkopeaefadkdjcmnicgpcncc). + +For more information, visit our website at https://vibinex.com/. + +## Setup Instructions + +To run Vibi-DPU locally: + +1. Generate public url using ngrok - `ngrok http 3000` +2. Fire up cloud sql proxy - `./cloud-sql-proxy --port 5432 vibi-test-394606:asia-south1:test-db` +3. Change url in team-monitor-website in .env.local - `NEXTAUTH_URL=https://example.ngrok-free.app` +4. Start team-monitor-website - `npm run dev` +5. Build vibi-dpu, go to vibi-dpu/vibi-dpu and run - `cargo build` +6. Go up to the root directory of vibi-dpu - `cd ../` +7. **Build the Docker image**: In the root directory of the project, run the following command to build a Docker image with the name "dpu". + + ```bash + docker build \ + --build-arg GCP_CREDENTIALS=/path/to/your/keyfile.json \ + --build-arg TOPIC_NAME=my-topic-name \ + --build-arg SUBSCRIPTION_NAME=my-subscription-name \ + --build-arg BITBUCKET_CLIENT_ID=your-bitbucket-client-id \ + --build-arg BITBUCKET_CLIENT_SECRET=your-bitbucket-client-secret \ + --build-arg BITBUCKET_BASE_URL=your-bitbucket-base-url \ + --build-arg INSTALL_ID=your-install-id \ + --build-arg SERVER_URL=your-server-url \ + -t dpu . + ``` +8. **Run the Docker container**: After building the image, you can run it using the following command. + + ```bash + docker run dpu + ``` + +## Contributing + +We welcome contributions from the community! Please read our contributing guidelines before submitting a pull request. + +## License + +This project is licensed under the terms of the GNU-GPLv3. \ No newline at end of file From e831f1d225928e4b82b5aaf1e5baac43862f7688 Mon Sep 17 00:00:00 2001 From: Tapish Rathore Date: Wed, 20 Sep 2023 15:49:13 +0530 Subject: [PATCH 05/11] Add instructions about oauth and setup --- README.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 23abdc5d..0a1c3b2c 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,15 @@ For more information, visit our website at https://vibinex.com/. To run Vibi-DPU locally: 1. Generate public url using ngrok - `ngrok http 3000` -2. Fire up cloud sql proxy - `./cloud-sql-proxy --port 5432 vibi-test-394606:asia-south1:test-db` -3. Change url in team-monitor-website in .env.local - `NEXTAUTH_URL=https://example.ngrok-free.app` -4. Start team-monitor-website - `npm run dev` -5. Build vibi-dpu, go to vibi-dpu/vibi-dpu and run - `cargo build` -6. Go up to the root directory of vibi-dpu - `cd ../` -7. **Build the Docker image**: In the root directory of the project, run the following command to build a Docker image with the name "dpu". +2. Paste this in OAuth consumers in callback_url field. +3. Clone [team-monitor-webiste](https://github.com/Alokit-Innovations/team-monitor-website/) locally. +4. Paste the client id and secret in team-monitor-wesite in .env.local in root directory. Also use them in the docker command below. +5. Fire up cloud sql proxy - `./cloud-sql-proxy --port 5432 vibi-test-394606:asia-south1:test-db` +6. Change url in team-monitor-website in .env.local - `NEXTAUTH_URL=https://example.ngrok-free.app` +7. Start team-monitor-website - `npm run dev` +8. Build vibi-dpu, go to vibi-dpu/vibi-dpu and run - `cargo build` +9. Go up to the root directory of vibi-dpu - `cd ../` +10. **Build the Docker image**: In the root directory of the project, run the following command to build a Docker image with the name "dpu". ```bash docker build \ @@ -32,11 +35,14 @@ To run Vibi-DPU locally: --build-arg SERVER_URL=your-server-url \ -t dpu . ``` -8. **Run the Docker container**: After building the image, you can run it using the following command. +11. **Run the Docker container**: After building the image, you can run it using the following command. ```bash docker run dpu ``` +12. For bitbucket, replace your url in this url and paste it on your browser and visit it. If you are using ngrok, you might get a "visit site" ngrok welcome page. Click and visit site. Grant any permissions asked from your user to bitbucket. Example URL - `https://bitbucket.org/site/oauth2/authorize?response_type=code&client_id=raFykYJRvEBHPttQAm&redirect_uri=https%3A%2F%2F5bef-171-76-86-89.ngrok-free.app%2Fapi%2Fbitbucket%2Fcallbacks%2Finstall&scope=repository%20pullrequest%20pullrequest:write%20webhook%20account%20repository:write`. You only need to replace the `5bef-171-76-86-89.ngrok-free.app` part with your own ngrok url instead of generating a new formatted url. +13. This would start the "setting up" part of dpu, where it calls bitbucket apis and collects repo info, user info, workspace info and pr info. +14. Next begin your testing. For instance, if you push to a PR, you should be able to see logs in next server, in dpu and see the required actions being performed on the PR. ## Contributing From 18abb76f291ead01b7e7dc556262e1958435c2fd Mon Sep 17 00:00:00 2001 From: Tapish Rathore Date: Wed, 20 Sep 2023 15:50:34 +0530 Subject: [PATCH 06/11] Revert "Add setup instructions and description to Readme" --- README.md | 49 ++----------------------------------------------- 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 23abdc5d..922405e8 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,2 @@ -# Vibi-DPU - -Vibi-DPU is an application written in Rust and packaged as a Docker image. It runs on the users' infrastructure to analyze private Intellectual Property data, empowering users through analysis without sacrificing privacy. - -The application communicates with our Next.js server, hosted on our infrastructure, which is also open source. You can find it [here](https://github.com/Alokit-Innovations/team-monitor-website/). - -Currently, we analyze code in Git repositories, and are soon planning to add APM data and business events. The insights we get from these analyses are communicated through comments/actions on pull requests and through our [open-source Chrome Extension](https://chrome.google.com/webstore/detail/vibinex-code-review/jafgelpkkkopeaefadkdjcmnicgpcncc). - -For more information, visit our website at https://vibinex.com/. - -## Setup Instructions - -To run Vibi-DPU locally: - -1. Generate public url using ngrok - `ngrok http 3000` -2. Fire up cloud sql proxy - `./cloud-sql-proxy --port 5432 vibi-test-394606:asia-south1:test-db` -3. Change url in team-monitor-website in .env.local - `NEXTAUTH_URL=https://example.ngrok-free.app` -4. Start team-monitor-website - `npm run dev` -5. Build vibi-dpu, go to vibi-dpu/vibi-dpu and run - `cargo build` -6. Go up to the root directory of vibi-dpu - `cd ../` -7. **Build the Docker image**: In the root directory of the project, run the following command to build a Docker image with the name "dpu". - - ```bash - docker build \ - --build-arg GCP_CREDENTIALS=/path/to/your/keyfile.json \ - --build-arg TOPIC_NAME=my-topic-name \ - --build-arg SUBSCRIPTION_NAME=my-subscription-name \ - --build-arg BITBUCKET_CLIENT_ID=your-bitbucket-client-id \ - --build-arg BITBUCKET_CLIENT_SECRET=your-bitbucket-client-secret \ - --build-arg BITBUCKET_BASE_URL=your-bitbucket-base-url \ - --build-arg INSTALL_ID=your-install-id \ - --build-arg SERVER_URL=your-server-url \ - -t dpu . - ``` -8. **Run the Docker container**: After building the image, you can run it using the following command. - - ```bash - docker run dpu - ``` - -## Contributing - -We welcome contributions from the community! Please read our contributing guidelines before submitting a pull request. - -## License - -This project is licensed under the terms of the GNU-GPLv3. \ No newline at end of file +# vibi-dpu +Vibinex Data Processing Unit for collecting and analyzing private data From eb01845b626925bf03ac93e2e82007a4eac53ba3 Mon Sep 17 00:00:00 2001 From: Tapish Rathore Date: Wed, 20 Sep 2023 16:15:41 +0530 Subject: [PATCH 07/11] Add what happens on port 3000 in instructions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0a1c3b2c..4c656d68 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ For more information, visit our website at https://vibinex.com/. To run Vibi-DPU locally: -1. Generate public url using ngrok - `ngrok http 3000` +1. Generate public url using ngrok - `ngrok http 3000`. We will run our next server locally on port 3000 in later steps. 2. Paste this in OAuth consumers in callback_url field. 3. Clone [team-monitor-webiste](https://github.com/Alokit-Innovations/team-monitor-website/) locally. 4. Paste the client id and secret in team-monitor-wesite in .env.local in root directory. Also use them in the docker command below. From 850d2489ef8d3bdd59f02a5b199143e264af2a00 Mon Sep 17 00:00:00 2001 From: Muskan Paliwal Date: Wed, 4 Oct 2023 09:13:26 +0530 Subject: [PATCH 08/11] added trigger to run the bitbucket flow test script from main repository --- cloudbuild.yaml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 cloudbuild.yaml diff --git a/cloudbuild.yaml b/cloudbuild.yaml new file mode 100644 index 00000000..bea6f4b3 --- /dev/null +++ b/cloudbuild.yaml @@ -0,0 +1,22 @@ +steps: +# Clone the main repository +- name: 'gcr.io/cloud-builders/git' + args: ['clone', '--recursive', 'https://github.com/Alokit-Innovations/test-scripts.git'] + +# Install Python dependencies +- name: 'gcr.io/cloud-builders/python' + args: ['pip', 'install', '-r', 'requirements.txt'] + dir: 'test-scripts' + +# Run the script from the main repository +- name: 'gcr.io/cloud-builders/python' + args: ['on-prem-bitbucket-tests.py'] + dir: 'test-scripts' + env: + - 'test_oauth_consumer_key=${_TEST_OAUTH_CONSUMER_KEY}' + - 'test_oauth_consumer_secret=${_TEST_OAUTH_CONSUMER_SECRET}' + - 'test_db_host=${_TEST_DB_HOST}' + - 'test_db_port=${_TEST_DB_PORT}' + - 'test_db_name=${_TEST_DB_NAME}' + - 'test_db_user=${_TEST_DB_USER}' + - 'test_db_password=${_TEST_DB_PASSWORD}' \ No newline at end of file From b5da7cc6420b80bb7dff10b96683c38b2f568c30 Mon Sep 17 00:00:00 2001 From: Muskan Paliwal Date: Wed, 4 Oct 2023 10:06:43 +0530 Subject: [PATCH 09/11] renamed cloudbuild.yaml file to bitbucket-test-trigger.yaml --- cloudbuild.yaml => bitbucket-test-trigger.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cloudbuild.yaml => bitbucket-test-trigger.yaml (100%) diff --git a/cloudbuild.yaml b/bitbucket-test-trigger.yaml similarity index 100% rename from cloudbuild.yaml rename to bitbucket-test-trigger.yaml From d2c98619a6941d5245580f12501b2649b1bdb86a Mon Sep 17 00:00:00 2001 From: Tapish Rathore Date: Wed, 18 Oct 2023 19:44:37 +0530 Subject: [PATCH 10/11] add user email instead of display_name in blamevec --- vibi-dpu/src/utils/gitops.rs | 63 +++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/vibi-dpu/src/utils/gitops.rs b/vibi-dpu/src/utils/gitops.rs index 6b64158a..97380413 100644 --- a/vibi-dpu/src/utils/gitops.rs +++ b/vibi-dpu/src/utils/gitops.rs @@ -4,6 +4,7 @@ use std::str; use serde::Deserialize; use serde::Serialize; use sha256::digest; +use uuid::timestamp; use crate::bitbucket::auth::refresh_git_auth; use crate::bitbucket::user::get_commit_bb; @@ -352,7 +353,7 @@ pub async fn generate_blame(review: &Review, linemap: &HashMap, review: &Review) -> Option> { +async fn process_blameitem(path: &str, linenum: &str, blamelines: Vec<&str>) -> Option> { let linenumint_res = linenum.parse::(); let mut blamevec = Vec::::new(); if linenumint_res.is_err() { @@ -371,8 +372,7 @@ async fn process_blameitem(path: &str, commit: &str, linenum: &str, blamelines: return None; } let linenumint = linenumint_res.expect("Uncaught error in linenumint_res"); - let lineauthormap = process_blamelines(&blamelines, linenumint, - &review.repo_name(), &review.repo_owner()).await; + let lineauthormap = process_blamelines(&blamelines, linenumint).await; let mut linebreak = linenumint; for lidx in linenumint..(linenumint + blamelines.len()-1) { if lineauthormap.contains_key(&lidx) && lineauthormap.contains_key(&(lidx+1)) { @@ -405,21 +405,54 @@ async fn process_blameitem(path: &str, commit: &str, linenum: &str, blamelines: return Some(blamevec); } -async fn process_blamelines(blamelines: &Vec<&str>, linenum: usize, - repo_name: &str, repo_owner: &str) -> HashMap { +async fn process_blamelines(blamelines: &Vec<&str>, linenum: usize) -> HashMap { let mut linemap = HashMap::::new(); for lnum in 0..blamelines.len() { let ln = blamelines[lnum]; let wordvec: Vec<&str> = ln.split(" ").collect(); - let commit = wordvec[0]; - let lineitem_opt = get_commit_bb(commit, repo_name, repo_owner).await; - if lineitem_opt.is_some() { - let lineitem = lineitem_opt.expect("Empty linemap_opt"); - linemap.insert( - linenum + lnum, - lineitem - ); - } + let (author, idx) = extract_author(&wordvec); + let timestamp = extract_timestamp(&wordvec, idx); + let lineitem = LineItem::new(author, timestamp); + linemap.insert( + linenum + lnum, + lineitem + ); } return linemap; +} + +fn extract_author(wordvec: &Vec<&str>) -> (String, usize) { + let mut author = wordvec[1]; + let mut idx = 1; + // Check if the second value is an email address (enclosed in angle brackets) + if !author.starts_with('(') && !author.ends_with('>') { + // Shift the index to the next non-empty value + while idx < wordvec.len() && (wordvec[idx] == "" || !wordvec[idx].starts_with('(')){ + idx += 1; + } + if idx < wordvec.len() { + author = wordvec[idx]; + } + } else { + // Remove the angle brackets from the email address + author = author.trim_start_matches('<').trim_end_matches('>'); + } + let authorstr = author.replace("(", "") + .replace("<", "") + .replace(">", ""); + return (authorstr, idx) +} + +fn extract_timestamp(wordvec: &Vec<&str>, mut idx: usize) -> String { + let mut timestamp = wordvec[2]; + if timestamp == "" || timestamp.starts_with('(') { + idx = idx + 1; + while idx < wordvec.len() && (wordvec[idx] == "" || wordvec[idx].starts_with('(')) { + idx = idx + 1; + } + if idx < wordvec.len() { + timestamp = wordvec[idx]; + } + } + return timestamp.to_string(); } \ No newline at end of file From a75c5488c071aca52b64ddf2903ef8993ac72e97 Mon Sep 17 00:00:00 2001 From: Tapish Rathore Date: Wed, 18 Oct 2023 19:48:29 +0530 Subject: [PATCH 11/11] remove unused imports --- vibi-dpu/src/utils/gitops.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/vibi-dpu/src/utils/gitops.rs b/vibi-dpu/src/utils/gitops.rs index 97380413..746f7246 100644 --- a/vibi-dpu/src/utils/gitops.rs +++ b/vibi-dpu/src/utils/gitops.rs @@ -4,10 +4,8 @@ use std::str; use serde::Deserialize; use serde::Serialize; use sha256::digest; -use uuid::timestamp; use crate::bitbucket::auth::refresh_git_auth; -use crate::bitbucket::user::get_commit_bb; use super::hunk::BlameItem; use super::review::Review;