diff --git a/Cargo.lock b/Cargo.lock index 3243c76..39731a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -537,7 +537,7 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "migrate-bb-to-gh" -version = "0.6.0" +version = "0.7.0" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 2421daf..057bf38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,9 @@ [package] name = "migrate-bb-to-gh" -authors = ["Arkadiusz Żmudzin "] -version = "0.6.0" +authors = ["Arkadiusz Żmudzin "] +version = "0.7.0" edition = "2021" +license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -24,3 +25,6 @@ tokio = { version = "1.17.0", features = ["full"] } [build-dependencies] base64 = "0.13.0" + +[features] +circleci = [] \ No newline at end of file diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..a7e77cb --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..bcc7905 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Arkadiusz Żmudzin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..21bd634 --- /dev/null +++ b/README.md @@ -0,0 +1,170 @@ +

Welcome to migrate-bb-to-gh 👋

+

+ Version + + License: MIT + +

+ +> A CLI tool for migration of repositories from Bitbucket to GitHub for organizations +> +> `migrate-bb-to-gh` guides you through migration process by interactive wizard, +> where you can select what repositories should be moved to the GitHub account + +### 🏠 [Homepage](https://github.com/arkus7/migrate-bb-to-gh) + +## Configuration +This project uses `config.yml` file stored inside the root of the project directory for including configuration (such as API keys, tokens etc.) inside the binary itself. + +
+This is a security concern! (click to expand) + +Usually, you wouldn't put any secrets inside the binary file, as it's rather easy to extract them from the binary. + +This setup enables to build the binary file with common configuration, without a need to have the config file next to a binary. + +**If you want to share the build binary, share it only with people you trust.** +
+ +The configuration file contents are _"encrypted"_ before they are put inside binary (if you're curious how, check [build.rs file](build.rs)). + +Example configuration file can be found in [sample.config.yml](./sample.config.yml) file. + +Config file contains: +- Git configuration: + - SSH key used to pull repositories from Bitbucket organization + - SSH key used to push repositories to GitHub organization +- Bitbucket configuration: + - username and app password of your Bitbucket account (used to fetch information about projects and repositories) +- GitHub configuration: + - username and personal access token of your GitHub account (used to manage repositories and teams in GitHub organization) + +Optionally, when you want to use the `circleci` feature, which gives you additional commands +to move your CircleCI configuration between Bitbucket and GitHub organizations, you'll need: +- CircleCI personal token of the admin account (it should be the same account for Bitbucket and GitHub) +- Bitbucket's organization ID on CircleCI +- GitHub's organization ID on CircleCI + +## Building + +In order to build a binary executable, you need to have Rust installed. + +Follow [the official guide](https://www.rust-lang.org/tools/install) on how to install Rust on your machine. + +When you have Rust installed (with Cargo), simply run the following command to build the binary + +```sh +cargo build --release +``` + +This will produce a single binary file called `migrate-bb-to-gh` in `target/release` directory. + +### Features + +This project has one feature, named `circleci` which gives you additional command in the CLI +to migrate CircleCI configuration between Bitbucket and GitHub project in CircleCI. + +If you'd like to use this feature, you need to build the binary with `--features circleci` option: + +```sh +cargo build --features circleci --release +``` + +### Troubleshooting + +In case you see an error similar to the one below: +```text +error: couldn't read config.yml: No such file or directory (os error 2) + --> build.rs:5:18 + | +5 | let config = include_bytes!("config.yml"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: could not compile `migrate-bb-to-gh` due to previous error +``` +make sure your `config.yml` file is in the root directory of the project (not inside `src`). + +## Usage + +You can list available commands by using `--help` option + +```sh +./migrate-bb-to-gh --help +``` + +### Wizard + +First, you need to go through a `wizard`, which will ask you to select repositories you want to migrate from Bitbucket, +letting you select who in your GitHub organization should have access to the selected repositories +(using [Teams](https://docs.github.com/en/organizations/organizing-members-into-teams/about-teams)), and change the default branch. + +```shell +./migrate-bb-to-gh wizard +``` + +The wizard results with a migration file named (by default) `migration.json`, +which contains all the details about what needs to be done during the migration. +You can inspect the file to see what will be done when the migration starts. + +At the end of the wizard, the CLI will list all the actions in human-readable form, so you can review it there as well. + +You can change the default name of the created file by providing an `--output` (or `-o`) option, passing a path to a file where it should be stored. + +```shell +./migrate-bb-to-gh wizard --output my-migration-file.json +``` + +If the migration file already exists, the `wizard` command will ask if you want to override it or not. +Not overriding file in this case results with cancellation of the wizard. + +### Migrate + +When your `migration.json` file is ready, you can start the migration by using `migrate` command, +passing the path to the migration file as a positional argument: + +```shell +./migrate-bb-to-gh migrate migration.json +``` + +Similar to the end of the `wizard` command, CLI will print a list of actions that will be taken during the migration. +You need to confirm whether the CLI should start the migration. + +The `migrate` command (apart from first confirmation) is not interactive. + +### CircleCI commands (with `circleci` feature) + +The project has a optional `circleci` feature (check [Features](#features) section to see how to enable it), +which gives you additional `circleci` command. + +The `circleci` command, similarly to the main app, has 2 subcommands: +- `wizard` which guides you through migration of CircleCI configuration from Bitbucket to GitHub +- `migrate` which executes the migration + +You can run them as shown below +```shell +./migrate-bb-to-gh circleci wizard +# wizard creates (by default) `ci-migration.json` file (can be changed with --output option) +./migrate-bb-to-gh circleci migrate ci-migration.json +``` + +## Author + +👤 **Arkadiusz Żmudzin** + +* GitHub: [@arkus7](https://github.com/arkus7) +* LinkedIn: [@arkadiusz-zmudzin](https://linkedin.com/in/arkadiusz-zmudzin) + +## Show your support + +Give a ⭐️ if this project helped you in any way! + +## 📝 License + +Copyright © 2022 [Arkadiusz Żmudzin](https://github.com/arkus7). + +This project is [MIT](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE) licensed. + +*** +_This README uses template from ❤️ [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_ \ No newline at end of file diff --git a/sample.config.yml b/sample.config.yml new file mode 100644 index 0000000..270c463 --- /dev/null +++ b/sample.config.yml @@ -0,0 +1,44 @@ +git: + # SSH key used to push repositories to GitHub + push_ssh_key: | + -----BEGIN OPENSSH PRIVATE KEY----- + ... + -----END OPENSSH PRIVATE KEY----- + # SSH key used to pull repositories from Bitbucket + pull_ssh_key: | + -----BEGIN OPENSSH PRIVATE KEY----- + ... + -----END OPENSSH PRIVATE KEY----- +bitbucket: + # username of Bitbucket user that should be used for fetching repositories from Bitbucket + username: some-bb-username + # app password created for the user above + # you can create app password at https://bitbucket.org/account/settings/app-passwords/ + # required permissions: projects:read, repositories:read + password: bb-user-token + # name of the organization from where the repositories should be fetched + # in case you're migrating repositories from a personal account, use your username + workspace_name: bb-org-name + +github: + # username of GitHub user that should be used for managing repositories in GitHub + username: gh-username + # personal access token of the above user + # you can create personal access token at https://github.com/settings/tokens + # required permissions: repo, admin:org + password: gh-personal-token + # name of the organization from where the repositories should be created in GitHub + # in case you're migrating repositories to a personal account, use your username + organization_name: gh-org-name + +### optional (only for `circleci` feature +circleci: + # personal access token from CircleCI + # you can create access token at https://app.circleci.com/settings/user/tokens + token: circleci-personal-token + # organization ID of Bitbucket CircleCI org + # you should be able to find it at https://app.circleci.com/settings/organization/bitbucket/ + bitbucket_org_id: circleci-bb-org-id + # organization ID of GitHub CircleCI org + # you should be able to find it at https://app.circleci.com/settings/organization/github/ + github_org_id: circleci-gh-org-id diff --git a/src/bitbucket.rs b/src/bitbucket.rs index 5a14f51..63a8951 100644 --- a/src/bitbucket.rs +++ b/src/bitbucket.rs @@ -124,6 +124,7 @@ impl BitbucketApi { Ok(branches) } + #[cfg(feature = "circleci")] pub async fn get_repository(&self, repo_name: &str) -> anyhow::Result> { let url = format!( "https://api.bitbucket.org/2.0/repositories/{repo_name}", diff --git a/src/circleci/api/mod.rs b/src/circleci/api/mod.rs index 09c416a..73998be 100644 --- a/src/circleci/api/mod.rs +++ b/src/circleci/api/mod.rs @@ -1,11 +1,14 @@ mod models; use crate::circleci::api::models::{ - ContextOwnerBody, CreateContextBody, ExportEnvironmentBody, PageResponse, StartPipelineBody, - UpdateContextVariableBody, + ContextOwnerBody, CreateContextBody, ExportEnvironmentBody, FollowProjectBody, + FollowProjectResponse, PageResponse, StartPipelineBody, UpdateContextVariableBody, }; use crate::config::CircleCiConfig; +use anyhow::Error; use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; +use reqwest::Url; +use serde::de::DeserializeOwned; use crate::api::{ApiClient, BasicAuth}; pub(crate) use models::{Context, ContextVariable, EnvVar}; @@ -87,9 +90,9 @@ impl CircleCiApi { org_id = self.org_id(vcs) ); - let res: PageResponse = self.get(url).await?; + let contexts = self.get_all_pages(&url).await?; - Ok(res.items) + Ok(contexts) } pub async fn get_context_variables( @@ -141,15 +144,36 @@ impl CircleCiApi { } pub async fn start_pipeline(&self, repo_name: &str, branch: &str) -> Result<(), anyhow::Error> { + let follow_resp = self.follow_project(repo_name, branch).await?; + + match follow_resp.first_build { + None => { + let url = format!( + "https://circleci.com/api/v2/project/gh/{repo_name}/pipeline", + repo_name = repo_name + ); + let body = StartPipelineBody { branch }; + let _: serde_json::Value = self.post(url, Some(body)).await?; + Ok(()) + } + Some(_) => Ok(()), + } + } + + async fn follow_project( + &self, + repo_name: &str, + branch: &str, + ) -> Result { let url = format!( "https://circleci.com/api/v1.1/project/gh/{repo_name}/follow", repo_name = repo_name ); - let body = StartPipelineBody { branch }; + let body = FollowProjectBody { branch }; - let _: serde_json::Value = self.post(url, Some(body)).await?; + let res: FollowProjectResponse = self.post(url, Some(body)).await?; - Ok(()) + Ok(res) } pub async fn create_context( @@ -188,6 +212,33 @@ impl CircleCiApi { Ok(var) } + async fn get_all_pages(&self, initial_url: &str) -> anyhow::Result> + where + T: DeserializeOwned, + { + let mut result = vec![]; + let mut url = initial_url.to_string(); + let parsed = Url::parse(initial_url)?; + let contains_query = parsed.query().is_some(); + + loop { + let response: PageResponse = self.get(url).await?; + result.extend(response.items); + + if let Some(next_page_token) = response.next_page_token { + if contains_query { + url = format!("{}&page-token={}", initial_url, next_page_token); + } else { + url = format!("{}?page-token={}", initial_url, next_page_token); + } + } else { + break; + } + } + + Ok(result) + } + fn org_id(&self, provider: VCSProvider) -> &str { match provider { VCSProvider::Bitbucket => &self.config.bitbucket_org_id, diff --git a/src/circleci/api/models.rs b/src/circleci/api/models.rs index ca1639a..ee59b02 100644 --- a/src/circleci/api/models.rs +++ b/src/circleci/api/models.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone)] pub(super) struct PageResponse { pub(crate) items: Vec, - next_page_token: Option, + pub(crate) next_page_token: Option, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -39,12 +39,14 @@ pub(super) struct StartPipelineBody<'a> { #[derive(Serialize, Deserialize, Debug, Clone)] pub(super) struct FollowProjectBody<'a> { - branch: &'a str, + pub(crate) branch: &'a str, } #[derive(Serialize, Deserialize, Debug, Clone)] pub(super) struct FollowProjectResponse { - first_build: Option, + pub(crate) first_build: Option, + pub(crate) following: bool, + pub(crate) workflow: Option, } #[derive(Serialize, Deserialize, Debug, Clone)] diff --git a/src/circleci/config.rs b/src/circleci/config.rs index 37baf81..4ff1145 100644 --- a/src/circleci/config.rs +++ b/src/circleci/config.rs @@ -1,5 +1,6 @@ use std::{collections::HashSet, str::FromStr}; +use crate::circleci::config::raw::JobEntry; use serde::{Deserialize, Serialize}; use self::raw::Context; @@ -24,7 +25,11 @@ impl FromStr for Config { raw::WorkflowEntry::Workflow(w) => w.jobs, _ => unreachable!(), }) - .flat_map(|j| j.into_values()) + .filter(|j| matches!(j, raw::JobEntry::Map(_))) + .flat_map(|j| match j { + JobEntry::Map(map) => map.into_values().collect::>(), + _ => unreachable!(), + }) .flat_map(|j| j.context) .for_each(|c| match c { Context::String(ctx) => { @@ -60,7 +65,14 @@ mod raw { #[derive(Debug, PartialEq, Serialize, Deserialize)] pub(crate) struct Workflow { - pub jobs: Vec>, + pub jobs: Vec, + } + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(untagged)] + pub(crate) enum JobEntry { + Map(BTreeMap), + Name(String), } #[derive(Debug, PartialEq, Serialize, Deserialize)] diff --git a/src/config.rs b/src/config.rs index df7c5a4..ab57468 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,6 +16,7 @@ fn decrypt_config(config_bytes: &[u8]) -> anyhow::Result> { pub struct Config { pub bitbucket: BitbucketConfig, pub github: GitHubConfig, + #[cfg(feature = "circleci")] pub circleci: CircleCiConfig, pub git: GitConfig, } @@ -34,6 +35,7 @@ pub struct GitHubConfig { pub organization_name: String, } +#[cfg(feature = "circleci")] #[derive(Serialize, Deserialize, Debug, Clone)] pub struct CircleCiConfig { pub token: String, diff --git a/src/github.rs b/src/github.rs index 9a65146..14efa9c 100644 --- a/src/github.rs +++ b/src/github.rs @@ -241,6 +241,7 @@ impl GithubApi { Ok(res) } + #[cfg(feature = "circleci")] pub async fn get_team_repositories(&self, team_slug: &str) -> anyhow::Result> { let url_factory = |page: u32| { format!( @@ -270,6 +271,7 @@ impl GithubApi { Ok(res) } + #[cfg(feature = "circleci")] pub async fn get_repo_branches(&self, full_repo_name: &str) -> anyhow::Result> { let url_factory = |page: u32| { format!( @@ -284,6 +286,7 @@ impl GithubApi { Ok(branches) } + #[cfg(feature = "circleci")] pub async fn get_file_contents( &self, full_repo_name: &str, diff --git a/src/lib.rs b/src/lib.rs index 9b3ecfd..878fdcf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,10 @@ mod api; mod bitbucket; -pub mod circleci; pub mod config; mod github; pub mod prompts; pub mod repositories; mod spinner; + +#[cfg(feature = "circleci")] +pub mod circleci; diff --git a/src/main.rs b/src/main.rs index 1d29c3c..bc55a75 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,12 @@ use std::path::PathBuf; use clap::{CommandFactory, Parser, Subcommand}; +#[cfg(feature = "circleci")] use migrate_bb_to_gh::circleci; use migrate_bb_to_gh::config; use migrate_bb_to_gh::repositories::{self, Migrator, Wizard}; -/// Utility tool for migration of repositories from Bitbucket to GitHub for Mood Up Team +/// Utility tool for migration of repositories from Bitbucket to GitHub for organizations #[derive(Parser)] #[clap(author, version, about, long_about = None)] #[clap(propagate_version = true)] @@ -33,6 +34,7 @@ enum Commands { #[clap(parse(from_os_str), value_name = "MIGRATION_FILE")] migration_file: PathBuf, }, + #[cfg(feature = "circleci")] /// Tool for migrating CircleCI configuration #[clap(name = "circleci")] CircleCi { @@ -41,6 +43,7 @@ enum Commands { }, } +#[cfg(feature = "circleci")] #[derive(Subcommand)] enum CircleCiCommands { /// Guides you through migration process, generating migration file for "migrate" subcommand @@ -93,6 +96,7 @@ async fn main() -> Result<(), anyhow::Error> { let migrator = Migrator::new(migration_file, version, config); let _ = migrator.migrate().await?; } + #[cfg(feature = "circleci")] Commands::CircleCi { command } => match &command { CircleCiCommands::Wizard { output } => { let res = circleci::Wizard::new(output, version, config).run().await?;