|
| 1 | +<!-- |
| 2 | +
|
| 3 | +@license Apache-2.0 |
| 4 | +
|
| 5 | +Copyright (c) 2025 The Stdlib Authors. |
| 6 | +
|
| 7 | +Licensed under the Apache License, Version 2.0 (the "License"); |
| 8 | +you may not use this file except in compliance with the License. |
| 9 | +You may obtain a copy of the License at |
| 10 | +
|
| 11 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 12 | +
|
| 13 | +Unless required by applicable law or agreed to in writing, software |
| 14 | +distributed under the License is distributed on an "AS IS" BASIS, |
| 15 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 16 | +See the License for the specific language governing permissions and |
| 17 | +limitations under the License. |
| 18 | +
|
| 19 | +--> |
| 20 | + |
| 21 | +# Package Migration |
| 22 | + |
| 23 | +> This document is intended to provide a step-by-step guide for moving a package from one part of the codebase to another part of the codebase. This guide also applies when renaming a package. |
| 24 | +
|
| 25 | +The following outlines a sequence of steps to follow in order to migrate a single strided API package which is in `stats/base` to `stats/strided`. |
| 26 | + |
| 27 | +At a high level, the process is as follows: |
| 28 | + |
| 29 | +1. Copy the existing package to a new location. |
| 30 | +2. Update all import paths which point to the original package to the new copy. |
| 31 | +3. Remove the original package. |
| 32 | + |
| 33 | +A more detailed sequence follows. It is **extremely important** to closely follow each step in order to avoid inadvertently breaking downstream packages within the project. |
| 34 | + |
| 35 | +## Steps |
| 36 | + |
| 37 | +### 0. Establish a clean working repository |
| 38 | + |
| 39 | +You should avoid performing a migration on a repository branch which has unstaged changes. Your repository should be in a clean and pristine state prior to performing a package migration. Because you will often be making changes which are randomly spread out across the entire codebase, you want to avoid a scenario where you unintentionally commit changes unrelated to the migration. |
| 40 | + |
| 41 | +Ensure that you create a new branch from the latest upstream `develop`. Assuming that your `upstream` is the main stdlib development repository (i.e., `https://github.com/stdlib-js/stdlib.git`), |
| 42 | + |
| 43 | +```bash |
| 44 | +git checkout develop |
| 45 | +git pull upstream develop |
| 46 | +git checkout -b <branch_name> |
| 47 | +``` |
| 48 | + |
| 49 | +where `<branch_name>` is a placeholder for the name of a branch on which you'll perform the migration (e.g., `migrate-foo-bar`). |
| 50 | + |
| 51 | +### 1. Copy package |
| 52 | + |
| 53 | +Copy the existing package to the desired location. Assuming you are in the top-level root project directory, |
| 54 | + |
| 55 | +```bash |
| 56 | +cp -R lib/node_modules/@stdlib/path/to/existing/package lib/node_modules/@stdlib/path/to/new/package |
| 57 | +``` |
| 58 | + |
| 59 | +For example, |
| 60 | + |
| 61 | +```bash |
| 62 | +cp -R lib/node_modules/@stdlib/stats/base/dmax lib/node_modules/@stdlib/stats/strided/dmax |
| 63 | +``` |
| 64 | + |
| 65 | +### 2. Update package contents |
| 66 | + |
| 67 | +Next, update the contents of the new package, which is likely to include the following: |
| 68 | + |
| 69 | +- The package name (`package.json`, `lib/index.js`, `README.md`, and any other files which include the name of the original package. |
| 70 | + |
| 71 | +- If a package contains `src` and `include` directories, |
| 72 | + |
| 73 | + - update the directory tree according to the path of the new package (e.g., `include/stdlib/stats/base/dmax` would become `include/stdlib/stats/strided/dmax`). |
| 74 | + - update header guards within header files in the `include` directory (e.g., `STDLIB_STATS_BASE_DMAX` would become `STDLIB_STATS_STRIDED_DMAX`). |
| 75 | + |
| 76 | +There may be other contents needing updating, so be sure to carefully inspect package contents. |
| 77 | + |
| 78 | +### 3. Compile any native files |
| 79 | + |
| 80 | +If a package contains a `src/addon.c` file, ensure that the package successfully compiles by running the following command |
| 81 | + |
| 82 | +```bash |
| 83 | +make install-node-addons NODE_ADDONS_PATTERN="path/to/new/package" |
| 84 | +``` |
| 85 | + |
| 86 | +For example, |
| 87 | + |
| 88 | +```bash |
| 89 | +make install-node-addons NODE_ADDONS_PATTERN="stats/strided/dmax" |
| 90 | +``` |
| 91 | + |
| 92 | +### 4. Run package unit tests and quality control commands |
| 93 | + |
| 94 | +To ensure that the new package works as intended, run the package's unit tests and other quality control commands. |
| 95 | + |
| 96 | +#### Unit tests |
| 97 | + |
| 98 | +```bash |
| 99 | +make test TESTS_FILTER=".*/path/to/new/package/.*" |
| 100 | +``` |
| 101 | + |
| 102 | +For example, |
| 103 | + |
| 104 | +```bash |
| 105 | +make test TESTS_FILTER=".*/stats/strided/dmax/.*" |
| 106 | +``` |
| 107 | + |
| 108 | +#### Examples |
| 109 | + |
| 110 | +```bash |
| 111 | +make examples EXAMPLES_FILTER=".*/path/to/new/package/.*" |
| 112 | +``` |
| 113 | + |
| 114 | +For example, |
| 115 | + |
| 116 | +```bash |
| 117 | +make examples EXAMPLES_FILTER=".*/stats/strided/dmax/.*" |
| 118 | +``` |
| 119 | + |
| 120 | +If a package contains C examples, compile and run the C examples. |
| 121 | + |
| 122 | +```bash |
| 123 | +make examples-c EXAMPLES_FILTER=".*/path/to/new/package/.*" |
| 124 | +``` |
| 125 | + |
| 126 | +For example, |
| 127 | + |
| 128 | +```bash |
| 129 | +make examples-c EXAMPLES_FILTER=".*/stats/strided/dmax/.*" |
| 130 | +``` |
| 131 | + |
| 132 | +#### Benchmarks |
| 133 | + |
| 134 | +```bash |
| 135 | +make benchmark BENCHMARKS_FILTER=".*/path/to/new/package/.*" |
| 136 | +``` |
| 137 | + |
| 138 | +For example, |
| 139 | + |
| 140 | +```bash |
| 141 | +make benchmark BENCHMARKS_FILTER=".*/stats/strided/dmax/.*" |
| 142 | +``` |
| 143 | + |
| 144 | +If a package contains C benchmarks, compile and run the C benchmarks. |
| 145 | + |
| 146 | +```bash |
| 147 | +make benchmark-c BENCHMARKS_FILTER=".*/path/to/new/package/.*" |
| 148 | +``` |
| 149 | + |
| 150 | +For example, |
| 151 | + |
| 152 | +```bash |
| 153 | +make benchmark-c BENCHMARKS_FILTER=".*/stats/strided/dmax/.*" |
| 154 | +``` |
| 155 | + |
| 156 | +### 5. Commit new package |
| 157 | + |
| 158 | +Provided all tests pass and source files successfully compile, commit the new package to your migration branch. |
| 159 | + |
| 160 | +```bash |
| 161 | +git add lib/node_modules/@stdlib/path/to/new/package && git commit |
| 162 | +``` |
| 163 | + |
| 164 | +For example, |
| 165 | + |
| 166 | +```bash |
| 167 | +git add lib/node_modules/@stdlib/stats/strided/dmax && git commit |
| 168 | +``` |
| 169 | + |
| 170 | +When writing the commit message, you should include the name of the package being added and, if there is a public issue related to this particular package migration, include a Git trailer which references that issue. |
| 171 | + |
| 172 | +For example, |
| 173 | + |
| 174 | +```text |
| 175 | +feat: add `stats/strided/dmax` |
| 176 | +
|
| 177 | +Ref: https://github.com/stdlib-js/stdlib/issues/4797 |
| 178 | +``` |
| 179 | + |
| 180 | +### 6. Remove the export of the original package from its parent namespace |
| 181 | + |
| 182 | +Next, open the `lib/index.js` file found in the parent namespace of the original package (e.g., `lib/node_modules/@stdlib/stats/base/lib/index.js`. |
| 183 | + |
| 184 | +If that file includes an exported symbol from the original package, remove it. For example, |
| 185 | + |
| 186 | +```diff |
| 187 | +- |
| 188 | +- /** |
| 189 | +- * @name dmax |
| 190 | +- * @memberof ns |
| 191 | +- * @readonly |
| 192 | +- * @type {Function} |
| 193 | +- * @see {@link module:@stdlib/stats/base/dmax} |
| 194 | +- */ |
| 195 | +- setReadOnly( ns, 'dmax', require( '@stdlib/stats/base/dmax' ) ); |
| 196 | +``` |
| 197 | + |
| 198 | +### 7. Commit the changes to the parent namespace |
| 199 | + |
| 200 | +If you removed an exported symbol from the parent namespace, commit those changes, making note that this is a breaking change. |
| 201 | + |
| 202 | +```bash |
| 203 | +git add lib/node_modules/@stdlib/path/to/original/parent/namespace && git commit |
| 204 | +``` |
| 205 | + |
| 206 | +For example |
| 207 | + |
| 208 | +```bash |
| 209 | +git add lib/node_modules/@stdlib/stats/base && git commit |
| 210 | +``` |
| 211 | + |
| 212 | +In your commit message, you should include a `BREAKING_CHANGE`, migration instructions, and a reference to any related public issues. For example, |
| 213 | + |
| 214 | +```text |
| 215 | +remove: remove `dmax` from namespace |
| 216 | +
|
| 217 | +This commit removes the `dmax` symbol from the `stats/base` |
| 218 | +namespace due to a package migration. |
| 219 | +
|
| 220 | +BREAKING CHANGE: remove `dmax` |
| 221 | +
|
| 222 | +To migrate, users should access the same symbol via the |
| 223 | +`stats/strided` namespace. |
| 224 | +``` |
| 225 | + |
| 226 | +### 8. Update paths using a global find-and-replace |
| 227 | + |
| 228 | +Next, perform a global find-and-replace to migrate all `require` (and `import`) paths which currently reference the original package to reference the new package. |
| 229 | + |
| 230 | +For example, the following `require` statement |
| 231 | + |
| 232 | +```text |
| 233 | +var dmax = require( 'stats/base/dmax' ); |
| 234 | +``` |
| 235 | + |
| 236 | +should become |
| 237 | + |
| 238 | +```text |
| 239 | +var dmax = require( 'stats/strided/dmax' ); |
| 240 | +``` |
| 241 | + |
| 242 | +A couple of very important notes to keep in mind when performing a global find-and-replace. |
| 243 | + |
| 244 | +- Be **very careful** to avoid erroneously updating the paths of packages whose names have a common prefix (e.g., `stats/base/dmaxabs`, `stats/base/dmaxsorted`, `stats/base/dmaxabssorted`). Those packages should **not** be inadvertently updated. |
| 245 | +- Additionally, ensure that, for packages having C implementations, if a package basename (e.g., `dmax`) has a hyphen, then downstream include paths also need to be updated. E.g., for package `stats/base/foo-bar` with include file `stats/base/foo_bar`, all downstream packages which include the previous header file need to be updated accordingly (e.g., `stats/strided/foo_bar`). |
| 246 | + |
| 247 | +### 9. Avoid updating original package and error database |
| 248 | + |
| 249 | +There are three packages where we do **not** want to update `require` paths. |
| 250 | + |
| 251 | +- The original package. The original package should remain working and keeps its original paths. |
| 252 | +- The global error database. The global error database is an append-only log. We need to avoid invalidating any existing references. |
| 253 | +- The REPL databases. Given the high velocity of stdlib development, updating these databases will create merge conflicts, which do not need to be immediately resolved. We can avoid the hassle of needing to rectify these conflicts by deferring to stdlib's daily cron job which automatically maintains and updates these databases. |
| 254 | + |
| 255 | +To dismiss any changes made to the above, run the following command |
| 256 | + |
| 257 | +```bash |
| 258 | +git checkout -- ./lib/node_modules/@stdlib/path/to/original/package && git checkout -- ./lib/node_modules/@stdlib/error && git checkout -- ./lib/node_modules/@stdlib/repl && git status |
| 259 | +``` |
| 260 | + |
| 261 | +For example, |
| 262 | + |
| 263 | +```bash |
| 264 | +git checkout -- ./lib/node_modules/@stdlib/stats/base/dmax && git checkout -- ./lib/node_modules/@stdlib/error && git checkout -- ./lib/node_modules/@stdlib/repl && git status |
| 265 | +``` |
| 266 | + |
| 267 | +After running the above, double-check the results of `git status` to check that the list of changed files matches expectation. |
| 268 | + |
| 269 | +### 10. Commit changes |
| 270 | + |
| 271 | +Now that you've cleaned up the path updates, commit the changes to your branch. |
| 272 | + |
| 273 | +```bash |
| 274 | +git add . && git commit |
| 275 | +``` |
| 276 | + |
| 277 | +As updating paths should not affect the behavior of downstream packages, your commit message should be a `refactor`, and you should include a reference to any public issue related to the migration. For example, |
| 278 | + |
| 279 | +```text |
| 280 | +refactor: update paths |
| 281 | +
|
| 282 | +Ref: https://github.com/stdlib-js/stdlib/issues/4797 |
| 283 | +``` |
| 284 | + |
| 285 | +### 11. Remove original package |
| 286 | + |
| 287 | +At this point, now that all downstream packages use the new package, we should be able to remove the original package from the project. |
| 288 | + |
| 289 | + |
| 290 | +```bash |
| 291 | +rm -rf lib/node_modules/@stdlib/path/to/original/package |
| 292 | +``` |
| 293 | + |
| 294 | +For example, |
| 295 | + |
| 296 | + |
| 297 | +```bash |
| 298 | +rm -rf lib/node_modules/@stdlib/stats/base/dmax |
| 299 | +``` |
| 300 | + |
| 301 | +### 12. Commit changes |
| 302 | + |
| 303 | +Commit the changes to your branch. |
| 304 | + |
| 305 | +```bash |
| 306 | +git add lib/node_modules/@stdlib/path/to/original/package && git commit |
| 307 | +``` |
| 308 | + |
| 309 | +For example, |
| 310 | + |
| 311 | +```bash |
| 312 | +git add lib/node_modules/@stdlib/stats/base/dmax && git commit |
| 313 | +``` |
| 314 | + |
| 315 | +In your commit message, you should include a `BREAKING_CHANGE`, migration instructions, and a reference to any related public issues. For example, |
| 316 | + |
| 317 | +```text |
| 318 | +remove: remove `stats/base/dmax` |
| 319 | +
|
| 320 | +This commit removes `@stdlib/stats/base/dmax` in favor of |
| 321 | +`@stdlib/stats/strided/dmax`. |
| 322 | +
|
| 323 | +BREAKING CHANGE: remove `stats/base/dmax` |
| 324 | +
|
| 325 | +To migrate, users should update their require/import paths to use |
| 326 | +`@stdlib/stats/strided/dmax` which provides the same API and implementation. |
| 327 | +
|
| 328 | +Ref: https://github.com/stdlib-js/stdlib/issues/4797 |
| 329 | +``` |
| 330 | + |
| 331 | +### 13. Push changes to remote repository |
| 332 | + |
| 333 | +At this point, you've completed a package migration. You should attempt to push your changes to your remote repository. |
| 334 | + |
| 335 | +```bash |
| 336 | +git push origin <branch_name> |
| 337 | +``` |
| 338 | + |
| 339 | +where `<branch_name>` is the name of the branch on which you've been working. |
| 340 | + |
| 341 | +If you made these changes on a fork, you should open a pull request against the `develop` branch on the main project [repository][stdlib-github]. |
| 342 | + |
| 343 | +* * * |
| 344 | + |
| 345 | +## Notes |
| 346 | + |
| 347 | +- Notice that every commit includes a `Ref:` link back to the RFC issue on the main project repository. This is useful for providing additional context regarding changes, especially those involving deprecations. |
| 348 | +- Provided you have properly setup your local repository (see the [contributing][stdlib-contributing] and [development][stdlib-development] guides), linting will be performed after every commit, and, prior to pushing changes to a remote repository, affected unit tests, examples, and benchmarks should automatically run. Depending on how widely used the original package was throughout stdlib, these quality control steps may take considerable time, and it is possible that unrelated lint errors may be flagged. If possible, address any failures, restage the changes, and attempt to commit or push again. |
| 349 | +- As mentioned above, be **very careful** when performing global find-and-replace. It can be easy to mistakenly update non-applicable paths, thus breaking packages and all downstream dependents. You've been warned. |
| 350 | + |
| 351 | +<section class="links"> |
| 352 | + |
| 353 | +[stdlib-github]: https://github.com/stdlib-js/stdlib |
| 354 | + |
| 355 | +[stdlib-contributing]: https://github.com/stdlib-js/stdlib/blob/develop/CONTRIBUTING.md |
| 356 | + |
| 357 | +[stdlib-development]: https://github.com/stdlib-js/stdlib/blob/develop/docs/contributing/development.md |
| 358 | + |
| 359 | +</section> |
| 360 | + |
| 361 | +<!-- /.links --> |
0 commit comments