publish: datatype parser #4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: "publish: datatype parser" | |
| # Independent publish + release for the standalone @clickhouse/datatype-parser | |
| # package (the data-type string parser). Like the per-package client publish | |
| # workflows (publish-client*.yml), it carries its own version in | |
| # packages/datatype-parser/package.json and ships on its own cadence. Triggered | |
| # manually, and — like those workflows — must be dispatched from the `release` | |
| # branch: the npm-publish environment is protected so only that branch may | |
| # deploy (the repo's human-in-the-loop release gate). Dispatches from any other | |
| # ref are skipped by the job-level `if` guard below. | |
| # | |
| # The release branch is itself protected, so the unit suite is not re-run here. | |
| # The `publish` job instead builds, packs the tarball, installs that exact | |
| # tarball into a throwaway project and smoke-tests its imports, and only then | |
| # publishes the same tarball with the "latest" tag (npm OIDC + provenance) and | |
| # pushes a matching git tag. The `e2e` job then repeats the smoke test against | |
| # the freshly published version on the registry across the supported Node | |
| # versions. | |
| permissions: | |
| contents: read | |
| id-token: write # Required for npm OIDC authentication and provenance | |
| concurrency: | |
| group: ${{ github.workflow }} | |
| cancel-in-progress: false | |
| on: | |
| workflow_dispatch: | |
| env: | |
| # Network resilience: npm's default of 2 fetch retries is not enough for | |
| # the transient registry errors (ECONNRESET) we regularly hit in CI. | |
| NPM_CONFIG_FETCH_RETRIES: "5" | |
| NPM_CONFIG_FETCH_RETRY_MINTIMEOUT: "10000" | |
| NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT: "60000" | |
| NPM_CONFIG_FETCH_TIMEOUT: "600000" | |
| jobs: | |
| publish: | |
| # The npm-publish environment only permits the release branch to deploy; | |
| # skip cleanly on any other ref instead of failing the protection check. | |
| if: github.ref == 'refs/heads/release' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| environment: npm-publish | |
| permissions: | |
| contents: write # Required to push the release git tag | |
| id-token: write # Required for npm OIDC authentication and provenance | |
| defaults: | |
| run: | |
| working-directory: packages/datatype-parser | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version: 24 | |
| registry-url: "https://registry.npmjs.org" | |
| - name: Install dependencies | |
| # datatype-parser is an npm workspace package, so install the whole | |
| # workspace from the repo root (overriding this job's package-dir | |
| # default). A scoped `npm ci` from the package dir still fires the root | |
| # lifecycle scripts (postinstall/parquet-wasm, prepare/husky) but | |
| # without their dev-only devDependencies, which breaks the install. The | |
| # remaining steps run from packages/datatype-parser as usual. | |
| working-directory: ${{ github.workspace }} | |
| run: npm ci | |
| - name: Build | |
| run: npm run build | |
| - name: Get the release version | |
| id: version | |
| run: | | |
| VERSION=$(node -p "require('./package.json').version") | |
| echo "Publishing @clickhouse/datatype-parser@$VERSION" | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Pack the tarball | |
| id: pack | |
| # prepack rebuilds dist before packing. | |
| run: | | |
| set -euo pipefail | |
| TARBALL=$(npm pack --pack-destination "$RUNNER_TEMP" | tail -1) | |
| echo "Packed: $TARBALL" | |
| echo "tarball=$RUNNER_TEMP/$TARBALL" >> "$GITHUB_OUTPUT" | |
| - name: Pre-publish smoke test (install the packed tarball) | |
| env: | |
| TARBALL: ${{ steps.pack.outputs.tarball }} | |
| run: | | |
| set -euo pipefail | |
| work="$(mktemp -d)" | |
| cd "$work" | |
| npm init -y >/dev/null 2>&1 | |
| npm install "$TARBALL" | |
| # Verify the main barrel entry resolves and exposes the parser, from | |
| # the exact artifact we are about to publish. | |
| node --input-type=module -e " | |
| import * as dt from '@clickhouse/datatype-parser'; | |
| if (typeof dt.parseDataType !== 'function') throw new Error('parseDataType missing from main export'); | |
| console.log('OK: packed tarball imports cleanly'); | |
| " | |
| - name: Publish to npm | |
| # Publish the exact tarball that passed the pre-publish smoke test. | |
| env: | |
| TARBALL: ${{ steps.pack.outputs.tarball }} | |
| run: npm publish "$TARBALL" --access public --provenance | |
| - name: Create and push release git tag | |
| env: | |
| RELEASE_TAG: datatype-parser-v${{ steps.version.outputs.version }} | |
| RELEASE_VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| if git ls-remote --exit-code --tags origin "refs/tags/${RELEASE_TAG}" >/dev/null 2>&1; then | |
| echo "Tag ${RELEASE_TAG} already exists on origin; skipping." | |
| exit 0 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git tag -a "${RELEASE_TAG}" -m "Release @clickhouse/datatype-parser ${RELEASE_VERSION}" | |
| git push origin "refs/tags/${RELEASE_TAG}" | |
| e2e: | |
| name: e2e (node ${{ matrix.node }}) | |
| needs: publish | |
| if: needs.publish.result == 'success' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| strategy: | |
| fail-fast: true | |
| matrix: | |
| node: [20, 22, 24, 26] | |
| env: | |
| PUBLISHED_VERSION: ${{ needs.publish.outputs.version }} | |
| steps: | |
| - name: Setup NodeJS ${{ matrix.node }} | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version: ${{ matrix.node }} | |
| registry-url: "https://registry.npmjs.org" | |
| - name: Wait for @clickhouse/datatype-parser@${{ needs.publish.outputs.version }} on npm | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${PUBLISHED_VERSION}" ]; then | |
| echo "PUBLISHED_VERSION is empty; cannot wait for npm publication." >&2 | |
| exit 1 | |
| fi | |
| pkg="@clickhouse/datatype-parser" | |
| # Poll the registry for up to ~5 minutes. New versions usually surface | |
| # in seconds, but the registry CDN can lag. | |
| max_attempts=60 | |
| sleep_seconds=5 | |
| attempt=1 | |
| echo "Waiting for ${pkg}@${PUBLISHED_VERSION} to be available on npm..." | |
| while true; do | |
| if npm view "${pkg}@${PUBLISHED_VERSION}" version >/dev/null 2>&1; then | |
| echo " ${pkg}@${PUBLISHED_VERSION} is available." | |
| break | |
| fi | |
| if [ "$attempt" -ge "$max_attempts" ]; then | |
| echo "Timed out waiting for ${pkg}@${PUBLISHED_VERSION} on npm" >&2 | |
| exit 1 | |
| fi | |
| echo " attempt ${attempt}/${max_attempts}: not available yet, sleeping ${sleep_seconds}s..." | |
| attempt=$((attempt + 1)) | |
| sleep "$sleep_seconds" | |
| done | |
| - name: Install and import the published package | |
| run: | | |
| set -euo pipefail | |
| work="$(mktemp -d)" | |
| cd "$work" | |
| npm init -y >/dev/null 2>&1 | |
| npm install "@clickhouse/datatype-parser@${PUBLISHED_VERSION}" | |
| # Verify the main barrel entry resolves and exposes the parser to a | |
| # downstream consumer. | |
| node --input-type=module -e " | |
| import * as dt from '@clickhouse/datatype-parser'; | |
| if (typeof dt.parseDataType !== 'function') throw new Error('parseDataType missing from main export'); | |
| console.log('OK: @clickhouse/datatype-parser@${PUBLISHED_VERSION} imports cleanly'); | |
| " |