Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/nightly-schedule.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ jobs:
permissions:
packages: write
contents: write
id-token: write
uses: ./.github/workflows/release.yml
secrets:
token: ${{ secrets.GITHUB_TOKEN }}
Expand Down
34 changes: 33 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ jobs:
permissions:
packages: write
contents: write
id-token: write
outputs:
api_image: ${{steps.set_image.outputs.api_image}}
migration_image: ${{steps.migration-publish.outputs.image}}
Expand All @@ -66,6 +67,13 @@ jobs:
distribution: 'temurin'
java-version: '11'
cache: 'gradle'
- name: setup node
uses: actions/setup-node@v5.0.0
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- name: setup npm
run: npm install --global npm@11.11.0
- name: Set version
id: version
env:
Expand Down Expand Up @@ -93,7 +101,27 @@ jobs:
- name: show version
run: echo ${{ steps.version.outputs.version }}
- name: build war
run: ./gradlew build --info --init-script init.gradle -PversionOverride=$VERSION
env:
VERSION: ${{ steps.version.outputs.version }}
run: ./gradlew build --info --init-script init.gradle "-PversionOverride=${VERSION}"
- name: Validate cwmsjs package
id: cwmsjs
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && steps.version.outputs.prerelease == 'false'
working-directory: clients/typescript/cwmsjs
run: |
PACKAGE_VERSION=$(node -p "require('./package.json').version")
echo "version=${PACKAGE_VERSION}" >> "$GITHUB_OUTPUT"

if VIEW_OUTPUT=$(npm view "cwmsjs@${PACKAGE_VERSION}" version 2>&1); then
echo "cwmsjs ${PACKAGE_VERSION} is already published; a retry will skip npm publish."
echo "published=true" >> "$GITHUB_OUTPUT"
elif grep -q "E404" <<< "${VIEW_OUTPUT}"; then
echo "published=false" >> "$GITHUB_OUTPUT"
npm publish --dry-run --access public --tag latest
else
echo "${VIEW_OUTPUT}" >&2
exit 1
fi
- name: Create GitHub Release
id: create_release
# Allow testing without creating a release
Expand All @@ -105,6 +133,10 @@ jobs:
generate_release_notes: true
prerelease: ${{steps.version.outputs.prerelease}}
token: ${{ secrets.token != null && secrets.token || secrets.GITHUB_TOKEN }}
- name: Publish cwmsjs to npm
if: steps.create_release.outcome == 'success' && steps.cwmsjs.outputs.published == 'false'
working-directory: clients/typescript/cwmsjs
run: npm publish --access public --tag latest
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4.1.0
- name: Docker meta
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/tagged-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ on:
- '[0-9][0-9][0-9][0-9].[0-9][0-9].[0-9][0-9]'
jobs:
release:
permissions:
contents: write
id-token: write
packages: write
uses: ./.github/workflows/release.yml
secrets:
token: ${{ secrets.GITHUB_TOKEN }}
Expand Down
1 change: 1 addition & 0 deletions clients/typescript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ In order to accommodate changes both to the generator and to CDA itself, cwmsjs
`[cwmsjs generator SemVer]-[CDA version/calver suffix]`

The Gradle build passes the CDA project version into the client package step. When running the package update script directly, set `CDA_CLIENT_VERSION_SUFFIX` or pass `--version-suffix=<version>`.
Zero-padded numeric CalVer identifiers are normalized for npm SemVer compatibility, so CDA release `2026.08.25` produces cwmsjs version `2.4.0-2026.8.25`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the 2.4.0- for/from in this example?

@krowvin krowvin Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2.4.0- is the current release of the cwmsjs generator side. Not the CDA instance.

You can read about the generator version choice here:
https://github.com/USACE/cwms-data-api/blob/develop/clients/typescript/README.md#versioning

https://hydrologicengineeringcenter.github.io/cwms-data-api-client-javascript/ - listed at the top of the last release.

Here's smore more background on this choice to have this:

A while back BK and I had decided we need a version for what we decide on the generator side vs what target of CDA that version was built against.

Here are some examples on why you might want to prefix with a semVar:

  1. CDA swagger spec is written such that every method would requre you to write CWMS in the function name. Version, I believe, 1 to 2 changed this so instead of
    cwmsjs.getCWMSTimeSeries you would type cwmsjs.getTimeSeries

  2. The generator lets you specify what sort of writing style/naming convention you wish to have for methods/classes.

If you try to query CDA without cwmsjs you will be forced to do things like tsGroupVar["assigned-time-series"].

However, the generator we have specified to use camelCase and in turn you can then use tsGroupVar.assignedTimeSeries. The distinction here is that they both could be a dot operator if it was tsGroupVar.value == tsGroupVar["value"].

But with dashes you are forced to use the brackets/quotes to access the value in javascript.

  1. The backend / underlying libraries used in the generator change. Perhaps we are using ajax, but then fetch improves and we can switch to that? (Or other libraries)

Changing that in the generator but having only 2026.8.26 as the version, set to CDA, would mean we would overwrite the other version instead of having 2.4.0 vs 2.5.0.

Hope that answers your question!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sort of, but I'm still not sure why we would still need it.


## Building CWMSjs from source

Expand Down
13 changes: 13 additions & 0 deletions clients/typescript/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ tasks.register('modPackage', NpxTask) {
outputs.file generatedClientDir.file('package.json')
}

tasks.register('testPackageMetadata', NpmTask) {
group 'verification'
description 'Test cwmsjs package metadata and version generation.'
dependsOn npmInstall
npmCommand = ['run', 'test:package']
inputs.files 'package.json', 'scripts/package-updates/modPackage.js',
'scripts/package-updates/modPackage.test.js'
}

tasks.register('postGenerate', NpxTask) {
group 'openapi'
description 'Apply source patches required after OpenAPI generation.'
Expand Down Expand Up @@ -155,3 +164,7 @@ tasks.named('build') {
dependsOn buildTypeScriptClient
dependsOn buildTypeScriptDocs
}

tasks.named('check') {
dependsOn testPackageMetadata
}
1 change: 1 addition & 0 deletions clients/typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"modPackage": "node ./scripts/package-updates/modPackage.js",
"modSpec": "node ./scripts/spec-updates/modSpec.js",
"postGenerate": "node ./scripts/postGenerate.js",
"test:package": "node --test ./scripts/package-updates/modPackage.test.js",
"link": "cd cwmsjs && npm link && cd ../tests && npm link cwmsjs"
},
"dependencies": {
Expand Down
45 changes: 36 additions & 9 deletions clients/typescript/scripts/package-updates/modPackage.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,39 @@ function writeJson(relativePath, value) {
);
}

function getVersionSuffixArg() {
function getVersionSuffixArg(argv = process.argv.slice(2)) {
const prefix = "--version-suffix=";
const value = process.argv.find((arg) => arg.startsWith(prefix));
return value ? value.slice(prefix.length) : process.argv[2];
const value = argv.find((arg) => arg.startsWith(prefix));
return value ? value.slice(prefix.length) : argv[0];
}

function getVersionSuffix() {
function normalizeVersionSuffix(value) {
if (typeof value !== "string" || value.trim() === "") {
throw new Error("Version suffix must be a non-empty string.");
}

return value
.trim()
.split(".")
.map((identifier) => {
if (!/^[0-9A-Za-z-]+$/.test(identifier)) {
throw new Error(`Invalid version suffix identifier: ${identifier}`);
}

return /^\d+$/.test(identifier)
? BigInt(identifier).toString()
: identifier;
})
.join(".");
}

function getVersionSuffix(argv = process.argv.slice(2), env = process.env) {
const explicitVersion =
getVersionSuffixArg() ||
process.env.CDA_CLIENT_VERSION_SUFFIX ||
process.env.CWMSJS_VERSION_SUFFIX;
getVersionSuffixArg(argv) ||
env.CDA_CLIENT_VERSION_SUFFIX ||
env.CWMSJS_VERSION_SUFFIX;
if (explicitVersion) {
return explicitVersion;
return normalizeVersionSuffix(explicitVersion);
}

throw new Error(
Expand Down Expand Up @@ -60,4 +80,11 @@ function main() {
);
}

main();
if (require.main === module) {
main();
}

module.exports = {
getVersionSuffix,
normalizeVersionSuffix,
};
50 changes: 50 additions & 0 deletions clients/typescript/scripts/package-updates/modPackage.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const assert = require("node:assert/strict");
const test = require("node:test");

const { getVersionSuffix, normalizeVersionSuffix } = require("./modPackage.js");

test("normalizes zero-padded production CalVer identifiers", () => {
assert.equal(normalizeVersionSuffix("2026.08.25"), "2026.8.25");
});

test("preserves a suffixed production release", () => {
assert.equal(normalizeVersionSuffix("2026.03.31-b"), "2026.3.31-b");
});

test("normalizes prerelease and nightly version suffixes", () => {
assert.equal(normalizeVersionSuffix("2026.08.25-dev"), "2026.8.25-dev");
assert.equal(normalizeVersionSuffix("develop-nightly"), "develop-nightly");
});

test("rejects empty and invalid version suffixes", () => {
assert.throws(() => normalizeVersionSuffix(""), /non-empty string/);
assert.throws(() => normalizeVersionSuffix("2026..25"), /Invalid version/);
assert.throws(
() => normalizeVersionSuffix("release/2026.08.25"),
/Invalid version/,
);
});

test("reads the CLI argument before environment variables", () => {
assert.equal(
getVersionSuffix(["--version-suffix=2026.08.25"], {
CDA_CLIENT_VERSION_SUFFIX: "2026.03.31",
}),
"2026.8.25",
);
});

test("reads supported environment variables", () => {
assert.equal(
getVersionSuffix([], { CDA_CLIENT_VERSION_SUFFIX: "2026.08.25-test" }),
"2026.8.25-test",
);
assert.equal(
getVersionSuffix([], { CWMSJS_VERSION_SUFFIX: "2026.08.25-dev" }),
"2026.8.25-dev",
);
});

test("requires an explicit version suffix", () => {
assert.throws(() => getVersionSuffix([], {}), /Missing version suffix/);
});
11 changes: 9 additions & 2 deletions clients/typescript/scripts/package-updates/updates.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
{
"description": "CWMS Data API Library for JavaScript/TypeScript created with OpenAPI generator for use with browser webapps",
"files": [
"dist/",
"src/",
"tsconfig.json",
"tsconfig.esm.json"
],
"unpkg": "./dist/cwmsjs.min.js",
"jsdelivr": "./dist/cwmsjs.min.js",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/esm/index.js",
"import": "./dist/cwmsjs.mjs",
"require": "./dist/index.js"
}
},
"scripts": {
"build": "tsc && tsc -p tsconfig.esm.json && npm run build:browser",
"build": "tsc && tsc -p tsconfig.esm.json && npm run build:esm && npm run build:browser",
"build:esm": "esbuild dist/esm/index.js --bundle --format=esm --platform=neutral --outfile=dist/cwmsjs.mjs",
"build:browser": "esbuild dist/esm/index.js --bundle --format=iife --global-name=cwmsjs --outfile=dist/cwmsjs.js && esbuild dist/esm/index.js --bundle --format=iife --global-name=cwmsjs --minify --sourcemap --outfile=dist/cwmsjs.min.js",
"prepare": "npm run build"
},
Expand Down
10 changes: 8 additions & 2 deletions clients/typescript/tests/smoke.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import {
OfficesApi,
ProjectsApi,
} from "../cwmsjs/dist/index.js";
import { createRequire } from "node:module";

const require = createRequire(import.meta.url);
const {
normalizeVersionSuffix,
} = require("../scripts/package-updates/modPackage.js");

if (!global.fetch) {
throw new Error(
Expand All @@ -29,7 +35,7 @@ async function main() {

let expectedVersion = process.env.EXPECTED_CWMSJS_VERSION;
if (!expectedVersion && process.env.CDA_CLIENT_VERSION_SUFFIX) {
expectedVersion = `${rootPackageJson.version}-${process.env.CDA_CLIENT_VERSION_SUFFIX}`;
expectedVersion = `${rootPackageJson.version}-${normalizeVersionSuffix(process.env.CDA_CLIENT_VERSION_SUFFIX)}`;
}

if (!expectedVersion) {
Expand All @@ -39,7 +45,7 @@ async function main() {
"utf8",
),
);
expectedVersion = `${rootPackageJson.version}-${rawSpec?.info?.version}`;
expectedVersion = `${rootPackageJson.version}-${normalizeVersionSuffix(rawSpec?.info?.version)}`;
}

if (packageJson.version !== expectedVersion) {
Expand Down
Loading