- ES2015 Transition
- Dependencies and Prerequisites
- Git Workflow
- Installation
- Configuration
- Running the Linter
- Running the App Locally
- Running on a VM
- Unit Tests
- Acceptance Tests
- Continuous Integration
- Releasing a New Version
- Miscellaneous Development Notes
The application was migrated from using Bower
, Grunt
and requirejs
to a structure following the ES2015 language
specification, using npm
to manage dependencies and webpack
to build
and bundle the application. The migration strategy was based on the "Treat
everything like a method" option described by
Ben McCormick.
- Node and npm
- An OpenSearch endpoint capable of receiving search queries and returning search results and related facets.
Dataset Search Services
provides the OpenSearch endpoint in the NSIDC environment. See the DSS
project to learn more about its dependencies, including Solr and Search Solr Tools.
A local DSS, DSS running in a pre-production environment (e.g., integration
,
qa
, or staging
), or the production version of DSS may be used as the
OpenSearch endpoint when running a local instance. See Configuration, below.
Development on this project uses the GitHub Flow:
- Create your feature branch (
git checkout -b my-new-feature
) - Stage your changes (
git add
) - Commit your ESLint-compliant (
npm run lint
) and test-passing changes (npm test
) with a good commit message (git commit
) - Push to the branch (
git push -u origin my-new-feature
) - Create a new Pull Request
-
Install node.js (Check out NVM if you need to move between versions of node for different projects.)
-
Install node modules:
npm install
The Dataset Catalog Services endpoint is configured in src/config/appConfig.js
.
Caveat: The pre-production DSS instances aren't created with valid
SSL certificates, so attempts to use those endpoints currently fail.
Run once with npm run lint
. Run automatically whenever a JavaScript file is changed
with npm run lint:watch
.
ESLint is configured in .eslintrc.json
, a JSON file containing a list of options
and allowed global variables.
There are currently two ways to run a development instance of this app: running it locally or deploying to a developer Drupal instance. The ability to deploy to a developer VM directly using puppet has been deprecated and removed.
Run a local server instance at http://localhost:8080
:
npm start
By default, the local server will set the environment to development
, and will
use the production OpenSearch endpoints. See the OpenSearch endpoint configuration
in src/config/appConfig.js
.
Note: As of August 2023 a bug associated with Drupal OS updates is preventing new VM builds.
Ansible and Garrison are used to deploy a new Drupal VM. The relevant repositories are:
-
ansible_drupal_nsidc_org) Note that this project is hosted in a private, local git repository.
-
If you need to modify the version of
search-interface
installed fromnpmjs.com
: nsidc-drupal8, specifically fileweb/libraries/package.json
.
Build steps:
-
Check out the
ansible_drupal_nsidc_org
project. -
Get a copy of the Drupal database as described in the Ansible Drupal project README.md file.
-
Follow the dev environment instructions in the "Deploy using garrison" section. The VM will be provisioned with the version of
search-interface
specified in thensidc-drupal8
projectstaging
branch, in fileweb/libraries/package.json
. -
Access the application on the resulting VM at:
http://dev.nsidc.org.docker-drupal8.USERNAME.dev.int.nsidc.org/data/search
Replace
USERNAME
with your LDAP username. Note that on the dev VM, some external images may not render.
If you've published a new version of search-interface
to npmjs.com
and want to build a
Drupal VM using that new version, create a temporary branch in the nsidc-drupal8
project
and modify the web/libraries/package.json
file to specify your desired version. Commit
the branch changes. Then change the Deploy using garrison step in the VM build process
that looks like:
vagrant nsidc ssh --no-tty --env=dev -c "/vagrant/local_garrison.sh -e staging -r staging"
to instead use your branch name:
vagrant nsidc ssh --no-tty --env=dev -c "/vagrant/local_garrison.sh -e my-new-branch -r my-new-branch"
Note that this option is for testing search-interface
changes that have been published
to npmjs.com
. See the next section if you need to test search-interface
changes that
have not yet been published to npmjs.com
.
If you want to deploy a work-in-progress search-interface
package to an existing
dev Drupal VM, copy the index.bundle.js
file to your dev Drupal machine as described below.
# Update the contents of search-interface/dist
cd /path-to-your-workspace/search-interface
npm run build # Or npm run build:dev if you want source maps for debugging
# Copy the updated index.bundle.js. Also copy the source map file, if you did
# "npm run build:dev" in the step above.
cd /path-to-your-workspace/ansible_drupal_nsidc_org
scp /path-to-your-workspace/search-interface/dist/index.bundle.js dev.nsidc.org.docker-drupal8.USERNAME.dev.int.nsidc.org:/home/vagrant/drupal/web/libraries/node_modules/@nsidc/search-interface/dist/index.bundle.js
vagrant nsidc ssh --no-tty --env=dev -c "cd /home/vagrant/drupal; lando drush cache:rebuild"
NOTE: The last step may not strictly be necessary, but if copying the file doesn't cause
the application to be updated, the lando drush...
should refresh everything.
Prior to the ES2015 update, unit tests were implemented using a grunt task, Karma,
and a HeadlessChrome environment. A few unit tests have already been migrated to
Jest, and are explicitly executed via the Test
configuration in .circleci/config.yml
and the test
and test:watch
script definitions in package.json
.
The remaining tests are now obsolete and need to be migrated to Jest
. See Jira stories
PSS-460 for some history and SRCH-76 for proposed work.
TODO: Migrate from Ruby-based tests to JS-based acceptance tests if
possible, and replace outdated grunt
tasks with npm tasks
to run the
acceptance test suite. The tests themselves haven't been updated to the ES2015
language specification, and are currently not usable.
See Jira stories SRCH-73 and related stories SRCH-41, SRCH-50.
The acceptance tests use the official
Ruby implementation of Cucumber,
and we use the tool RuboCop to lint the
Ruby code contained within this project. After installing the gems listed in
Gemfile
with bundle install
, RuboCop can be run with bundle exec rubocop
. Settings can be found in .rubocop.yml
.
- Firefox
- If using the development VM under Mac OS X, XQuartz is required.
To run the acceptance tests on your machine from the
development VM, you will first need
to ssh with X windowing enabled. Instead of connecting with vagrant ssh
, use
ssh -X [email protected] -p 2222
.
grunt test:acceptance --project=arctic-data-explorer --environment=integration
project
can be arctic-data-explorer
or nsidc_search
environment
can be integration
, qa
, or staging
.
The URL used by Cucumber is built from project
and environment
. To test
against a different URL (like when running the tests locally), specify with a
--url
flag (project
must still be specified):
grunt test:acceptance --url=http://localhost:8081 --project=arctic-data-explorer
CircleCI is configured to run eslint
and tests automatically when changes are committed
to the repository. See the configuration in .circleci/config.yml
You can run the CircleCI job(s) in your local workspace to confirm all steps pass before committing any changes. The steps are:
- Install the CircleCI CLI.
- Ensure Docker is running.
circleci config process .circleci/config.yml > process.yml
circleci local execute -c process.yml test
See The CircleCI documentation for more information.
Tl;dr: Adding a version tag to a branch in the format vNN.NN.NNN
(or vNN.NN.NN-rc.NN
, in
the case of release candidates) will package and publish that commit to
npmjs.com!
See also Version Handling below.
In brief, the steps are:
-
Confirm all tests pass, and that all changes have been committed and pushed to origin. Include a
CHANGELOG
entry with the textUnreleased
instead of a version number and date. -
Merge the feature branch into the main branch. Update your local working directory.
git checkout main && git pull
-
Bump the desired semver field (major, minor, patch), tag the main branch with the updated version number, and push changes to origin.
npm run release -- [major | minor | patch ]
NOTE: Mac Users - Make sure you are using a GNU-compliant version of sed
Versions should follow Semantic Versioning guidelines.
Adding a version tag to a branch in the format vNN.NN.NNN
(or vNN.NN.NN-rc.NN
, in
the case of release candidates) will package and publish that commit to
npmjs.com.
Note: The naming structure for the prerelease scripts (setup:prerelease
and bump:prerelease
)
are intentional in order to avoid unnecessarily triggering the release
script.
$ npm run setup:prerelease # Adds an initial pre-release tag to the current branch (e.g. v3.3.0-rc.0)
# Does not tag the branch. You'll need to push changes to git after running this step.
$ npm run bump:prerelease # Bumps the prerelease number (e.g. 3.3.0-rc.1) and tags the branch.
Running npm run bump:prerelease
will tag the current branch with an updated release candidate
version, the tag will be pushed to origin (via the postversion
trigger), and CircleCI will build
and publish a new package to npmjs
.
To add a release tag (no -rc.N
suffix):
npm run release -- [major | minor | patch ]
The release
script noted above is a shortcut for the built-in npm version
command.
Note the --
(two dashes surrounded by spaces) before the level specification. You can also do:
npm version [major | minor | patch ]
npm run release -- ...
will tag the current branch, the tag will be pushed to origin (via the
postversion
trigger), and CircleCI will build and publish a new package to npmjs
.
At a minimum, developers will need to create a PCT when a new version
is deployed to npmjs.com. Depending on the changes,
a pull request may also need to be submitted to update files in
nsidc-drupal8
(e.g. web/libraries/package.json
).
- The structure of our Sass code is as follows:
src/sass
_config_*.scss
- contains different variable definitions to separate styles across projects built from the common codebase
*_main.scss
- imports the configuration and modules for that project
modules/
- a Sass module defines the styling for a specific part of the page; keeping our code more modular makes it easier to identify places where it can be shared across projects, and makes it easier to find a particular selector you are interested in modifying or investigating
utilities/
- our own custom Sass functions; no CSS rules are defined in here
junk/
- old, monolithic CSS files converted to Sass syntax still needing
cleanup (break into smaller parts, put those parts in
modules/
)
- old, monolithic CSS files converted to Sass syntax still needing
cleanup (break into smaller parts, put those parts in
grunt sass:dev
generatessrc/css/ade-search.css
andsrc/css/nsidc-search.css
, combining the project-specific scss and the common files into onegrunt sass:$PROJECT
generatestmp/css/$PROJECT-search.css
; this is used bygrunt build:$PROJECT
(see the Build section)grunt sass
is configured to compress the generated CSS files and generate source maps, allowing tools like the Chrome Inspector to automatically show line numbers from the original Sass filesgrunt scsslint
runs the scss-lint tool on the Sass files. It is configured by rules inconfig/scss-lint.yml
(with additional rules that should be cleaned up inconfig/scsslint-todo.yml
)
When running some of the Grunt tasks, you may see some warning output like:
(node:29680) Warning: Accessing non-existent property 'cat' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
(node:29680) Warning: Accessing non-existent property 'cd' of module exports inside circular dependency
(node:29680) Warning: Accessing non-existent property 'chmod' of module exports inside circular dependency
(node:29680) Warning: Accessing non-existent property 'cp' of module exports inside circular dependency
This is apparently being emitted by the shelljs
package in the Node 14+ context.
To see more details and confirm the source set the NODE_OPTIONS
environment variable
to include --trace-warnings
. For example:
NODE_OPTIONS="--trace-warnings" grunt test:unit
If acceptance tests are failing with a warning about an invalid Xvnc password,
run vncpasswd
on the machine running the tests and set a dummy password
(password value doesn't matter).
Apparently NodeJS has added a new thing in package.json called "exports". A package author can use this section to declare what things are exported for use, and a map to where they are located in the package directory.
Webpack 5 now enforces this--if you attempt to import something which is not declared as exported from the package it is in, the import will fail. A package which omits the "exports" section defaults to everything being exported / importable. A package which includes the "exports" section only exports things listed, nothing else.
This has the following side-effect:
- Your code imports "foo" from package X
- Your code imports "bar" from package Y
- Package X has no "exports" section because the author(s) didn't know about this new feature
- Package Y does have an exports section, but it does not export "bar" (possibly because they didn't understand this new feature)
- Using webpack 4 is no problem, both imports work
- Upgrading to webpack to v5 results in the import of "foo" succeeding and the import of "bar" failing
In our case we were successfully importing CSS from vanillajs-datepicker, but when upgrading webpack to v5, the build started raising a new error indicating that the CSS could not be imported because the package (vanillajs-datepicker) did not export it.
A workaround in the webpack config which bypasses the export / import rules:
resolve: {
alias: {
'vjs-datepicker': path.resolve(__dirname, 'node_modules/vanillajs-datepicker/dist/css')
}
}
which allows the import of this form to work (currently used in TemporalCoverageView
):
import 'vjs-datepicker/datepicker-bs4.css';
If / when the vanillajs-datepicker package updates their exports to export this CSS, we can change the import back to:
import 'vanillajs-datepicker/dist/css/datepicker-bs4.css';