|
| 1 | +# CSV Bulk Project Import |
| 2 | + |
| 3 | +The CSV bulk import lets the user create multiple conversion projects at once by uploading a single CSV file. |
| 4 | + |
| 5 | +## How to Access |
| 6 | + |
| 7 | +1. Open the Backstage instance and navigate to `/create`. |
| 8 | +2. Select the **Chef-to-Ansible Conversion Project** template (`chef-conversion-project-template`). |
| 9 | +3. On the first page, choose **CSV upload** as the input method. |
| 10 | +4. Upload the CSV file and proceed through the wizard. |
| 11 | + |
| 12 | +The wizard asks for authentication with each SCM provider (GitHub, GitLab, Bitbucket) referenced in the CSV. Projects are created sequentially with the same permission checks as if the user had created each one individually. |
| 13 | + |
| 14 | +## CSV File Format |
| 15 | + |
| 16 | +The file must be UTF-8 encoded with a header row. Column order does not matter, but header names must match exactly. |
| 17 | + |
| 18 | +### Required Columns |
| 19 | + |
| 20 | +| Column | Description | |
| 21 | +| ------------------ | --------------------------------------------------------------------------------------------------------- | |
| 22 | +| `name` | Unique project name | |
| 23 | +| `abbreviation` | Short project identifier, 1-5 alphanumeric characters matching `^([a-zA-Z][a-zA-Z0-9]*)(-[a-zA-Z0-9]+)*$` | |
| 24 | +| `sourceRepoUrl` | URL of the repository containing the Chef cookbook to convert | |
| 25 | +| `sourceRepoBranch` | Branch to read from in the source repository | |
| 26 | +| `targetRepoBranch` | Branch to write converted Ansible output to | |
| 27 | + |
| 28 | +### Optional Columns |
| 29 | + |
| 30 | +| Column | Description | |
| 31 | +| --------------- | ---------------------------------------------------------------------------------------- | |
| 32 | +| `description` | Project description (defaults to empty) | |
| 33 | +| `ownedByGroup` | Backstage group that owns the project. When empty, the signed-in user becomes the owner. | |
| 34 | +| `targetRepoUrl` | Repository for converted output. Defaults to `sourceRepoUrl` when empty. | |
| 35 | + |
| 36 | +No extra columns are allowed -- the import will reject unknown headers. |
| 37 | + |
| 38 | +### Repeatable Import |
| 39 | + |
| 40 | +The CSV import is designed to be run repeatedly with the same or an updated file. Projects whose name already exists are **skipped** (not duplicated) and counted as "skipped" in the results summary. |
| 41 | + |
| 42 | +A typical workflow for a large import: |
| 43 | + |
| 44 | +1. Upload the CSV. Some projects succeed, some may fail (e.g. due to a missing repository or a typo). |
| 45 | +2. Review the results. The summary shows how many succeeded, failed, and were skipped. |
| 46 | +3. Fix the issues - correct the CSV rows that failed and, if a partially-created project needs to be recreated, delete it from the application first. |
| 47 | +4. Re-upload the corrected CSV. Already-created projects are skipped automatically. Only the new or corrected rows are processed. |
| 48 | + |
| 49 | +### Repository URL Format |
| 50 | + |
| 51 | +Both `sourceRepoUrl` and `targetRepoUrl` accept two formats. All URLs are normalized to HTTPS clone URLs before being stored. |
| 52 | + |
| 53 | +**Plain HTTPS URLs** (standard clone URLs): |
| 54 | + |
| 55 | +| Provider | Format | |
| 56 | +| --------- | -------------------------------------- | |
| 57 | +| GitHub | `https://github.com/owner/repo` | |
| 58 | +| GitLab | `https://gitlab.com/owner/repo` | |
| 59 | +| Bitbucket | `https://bitbucket.org/workspace/repo` | |
| 60 | + |
| 61 | +**Backstage RepoUrlPicker format** (query-parameter style, without `https://`): |
| 62 | + |
| 63 | +| Provider | Format | |
| 64 | +| --------- | ---------------------------------------------------------------- | |
| 65 | +| GitHub | `github.com?owner=myuser&repo=myrepo` | |
| 66 | +| GitLab | `gitlab.com?owner=myuser&repo=myrepo` | |
| 67 | +| Bitbucket | `bitbucket.org?workspace=myworkspace&project=myproj&repo=myrepo` | |
| 68 | + |
| 69 | +For Bitbucket, the `project` parameter is organizational metadata and is not part of the clone URL. Only `workspace` and `repo` are used. |
| 70 | + |
| 71 | +For self-hosted instances (e.g. GitHub Enterprise, self-hosted GitLab), the corresponding host should be used in place of the public domain. The host must be listed in the `integrations:` section of `app-config.yaml` so the plugin can detect the correct SCM provider. See [SCM Provider Detection](../README.md#scm-provider-detection). |
| 72 | + |
| 73 | +### Example |
| 74 | + |
| 75 | +```csv |
| 76 | +name,abbreviation,sourceRepoUrl,sourceRepoBranch,targetRepoUrl,targetRepoBranch,description,ownedByGroup |
| 77 | +web-app,wapp,https://github.com/myorg/web-app-chef,main,https://github.com/myorg/web-app-ansible,main,Convert web app cookbook,team-platform |
| 78 | +db-setup,dbset,gitlab.com?owner=myorg&repo=db-chef,develop,gitlab.com?owner=myorg&repo=db-ansible,main,, |
| 79 | +cache-svc,cache,bitbucket.org?workspace=myws&project=x2a&repo=cache-chef,main,,main,Cache service conversion, |
| 80 | +``` |
| 81 | + |
| 82 | +Notes on the example: |
| 83 | + |
| 84 | +- Row 1 (`web-app`): uses plain HTTPS URLs. |
| 85 | +- Row 2 (`db-setup`): uses RepoUrlPicker-style URLs for GitLab. `description` and `ownedByGroup` are left empty. |
| 86 | +- Row 3 (`cache-svc`): uses RepoUrlPicker-style URL for Bitbucket. `targetRepoUrl` is empty, so the source repository is used as the target. |
| 87 | + |
| 88 | +### CSV file template |
| 89 | + |
| 90 | +Download a [sample CSV file](../plugins/x2a-backend/public/sample-projects.csv) with all supported headers. |
| 91 | + |
| 92 | +At runtime, the file is served at `/x2a/download/sample-projects.csv` (via the frontend plugin route). |
| 93 | + |
| 94 | +## RepoAuthentication Scaffolder Extension |
| 95 | + |
| 96 | +When using CSV import, the template uses the `RepoAuthentication` custom scaffolder field to collect OAuth tokens for each SCM provider found in the CSV. This replaces the standard `RepoUrlPicker` used in manual mode. |
| 97 | + |
| 98 | +### How It Works |
| 99 | + |
| 100 | +1. The extension parses the uploaded CSV and identifies all distinct SCM providers across source and target URLs. |
| 101 | +2. It opens an OAuth dialog for each provider for the user to authenticate. |
| 102 | +3. Tokens are stored as scaffolder secrets with the prefix `OAUTH_TOKEN_` (e.g. `OAUTH_TOKEN_github`, `OAUTH_TOKEN_gitlab`, `OAUTH_TOKEN_bitbucket`). |
| 103 | +4. The wizard blocks progression until all required providers are authenticated. |
| 104 | + |
| 105 | +### Using in a Template |
| 106 | + |
| 107 | +The extension is registered as `RepoAuthentication` and is available as a `ui:field`. It must reference the CSV field via `ui:options.csvFieldName`: |
| 108 | + |
| 109 | +```yaml |
| 110 | +properties: |
| 111 | + repoAuthentication: |
| 112 | + type: string |
| 113 | + description: Provide login to all the SCMs relevant for the source CSV. |
| 114 | + ui:field: RepoAuthentication |
| 115 | + ui:options: |
| 116 | + csvFieldName: csvContent |
| 117 | +``` |
| 118 | +
|
| 119 | +### Registering the Extension |
| 120 | +
|
| 121 | +In a standard Backstage app, import and add the extension so the scaffolder can find it: |
| 122 | +
|
| 123 | +```typescript |
| 124 | +import { RepoAuthenticationExtension } from '@red-hat-developer-hub/backstage-plugin-x2a'; |
| 125 | + |
| 126 | +// In the App component, alongside <ScaffolderPage>: |
| 127 | +<ScaffolderFieldExtensions> |
| 128 | + <RepoAuthenticationExtension /> |
| 129 | +</ScaffolderFieldExtensions> |
| 130 | +``` |
| 131 | + |
| 132 | +For Red Hat Developer Hub (RHDH) with dynamic plugins, the extension is registered via configuration instead. See [Providing custom Scaffolder field extensions](https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.9/html/installing_and_viewing_plugins_in_red_hat_developer_hub/assembly-front-end-plugin-wiring.adoc_rhdh-extensions-plugins#con-providing-custom-scaffolder-field-extensions.adoc_assembly-front-end-plugin-wiring) in the RHDH documentation. |
0 commit comments