Skip to content

Generate ol-analytics-api's TypeScript client off release tags - #5995

Merged
blarghmatey merged 4 commits into
mainfrom
api-clients-tags
Sep 26, 2026
Merged

blarghmatey merged 4 commits into
mainfrom
api-clients-tags

Conversation

@blarghmatey

Copy link
Copy Markdown
Member

What are the relevant tickets?

N/A

Description (What does it do?)

ol-analytics-api publishes per-tenant OpenAPI specs as of
mitodl/ol-analytics-api#70, so its TypeScript client can
now be generated. It cuts releases as bare CalVer tags (2026.9.17.1) and keeps
no long-lived release branch, so there is nothing for the existing
release-branch topology to watch.

  • Adds an optional source_repo_tag_regex. When set, the source resource
    versions on tags (version_type: tags) instead of on spec changes landing on
    a branch.
  • Adds the ol-analytics-api entry to PIPELINE_CONFIGS.
  • client_repo_subpath now takes a list as well as a string, and the publish
    job emits one task per entry. ol-analytics-api's two tenants are independent
    APIs (an org-manager dashboard behind a user JWT, and a machine-to-machine
    partner integration), so they ship as separate packages rather than one that
    carries both.
  • Fixes the LoadVarStep, which read .git/refs/heads/<branch>. That path does
    not exist under a detached tag checkout. .git/ref is written under both
    version types used here, and is already what this repo reads elsewhere
    (k8s_apps/pipeline.py, simple_pulumi/pipeline.py). Not tag-specific; it
    was fragile before.

Two properties of tag versioning worth knowing, both also in the docstring. The
git resource consults paths only when versioning on commits, so under a tag
regex every matching tag rebuilds the client whether or not a spec actually
moved; "only when the interface changed" now comes from the release cadence
rather than the path filter. branch stops applying too, so a matching tag
anywhere in the repo triggers it.

How can this be tested?

uv run python -m ol_concourse.pipelines.libraries.api_clients_pipeline --variant <name>

The two existing variants are unaffected. Generating mitxonline and
mit-learn gives an unchanged source (still branch: release, still the
openapi/specs/*.yaml paths filter, no version_type) and exactly one publish
task against the same directory as before. Two things move for them: the
load_var file path, which resolves to the same SHA it read before, and the
publish task's name, which becomes publish-node-<package> because two tasks in
one plan cannot share a name. That is a step label in the Concourse UI, not
behavior.

For the new variant, generating it produces:

source: {"uri": "https://github.com/mitodl/ol-analytics-api", "branch": "main",
         "version_type": "tags", "fetch_tags": true,
         "tag_regex": "^[0-9]{4}\\.[0-9]{1,2}\\.[0-9]{1,2}\\.[0-9]+$"}
publish-node-ol-analytics-dashboard-api-axios
  -> ol-analytics-api-clients/src/typescript/ol-analytics-dashboard-api-axios
publish-node-ol-analytics-learner-records-api-axios
  -> ol-analytics-api-clients/src/typescript/ol-analytics-learner-records-api-axios

The regex is POSIX ERE, not PCRE, because the resource filters tags with
grep -E. \d there is not a digit class: GNU grep warns "stray \ before d",
drops the escape, and the pattern then requires literal d characters and
matches nothing. Checked by piping through /usr/bin/grep -E rather than a
language regex engine. All eight tags ol-analytics-api currently has match,
and hotfix/abc123, releases/2026.9.17.1, v2026.9.17.1, 2026.9.17,
latest and 2026.9.17.1-rc1 do not.

Generation raises if version_type did not reach the resource source, so an
ol-concourse too old to support it fails here rather than producing a pipeline
that quietly versions on commits:

RuntimeError: ol-analytics-api asked to version on tags, but the installed
ol-concourse dropped version_type from the resource source.

ruff check and mypy on the touched package are clean apart from two
pre-existing D100s, identical to main.

This cannot be validated against a running pipeline before merge, because there is no
client repo to point it at yet (see below).

Additional Context

Two things gate merging, which is why this is a draft:

  1. The version_type support this depends on
    (Let a git resource version on tags ol-concourse#108) is not released yet, and the
    ol-concourse>=0.18,<0.19 pin means merging the library PR is not enough on
    its own, because this repo still has to relock onto the release. git_repo does not
    reject the argument when the library is too old; it forwards it through
    **kwargs onto Resource as a top-level key, so source never gets it and
    the tag_regex sits inert, leaving a pipeline that versions commits on
    main with no paths filter. The guard above turns that into a generation
    error, but the relock is still a step someone has to take.

  2. mitodl/ol-analytics-api-clients does not exist yet. client_repo_uri
    points at it and the two client_repo_subpath entries have to match
    directories under src/typescript/ there, alongside one
    config/typescript-axios-*.yaml generator config per spec file. Until that
    repo exists this entry is inert; nothing sets the pipeline up automatically.

Checklist:

  • Let a git resource version on tags ol-concourse#108 merged and released
    (0.18.2 or later)
  • uv lock --upgrade-package ol-concourse here, so the pin actually
    resolves to that release (generation raises until it does)
  • mitodl/ol-analytics-api-clients created, with a generator config per
    spec and the two package directories named above

🤖 Generated with Claude Code

https://claude.ai/code/session_01CH3LZ41bD4ZKDnTQcviqb6

blarghmatey and others added 3 commits September 22, 2026 15:57
ol-analytics-api publishes per-tenant OpenAPI specs as of
mitodl/ol-analytics-api#70, but it cuts releases as bare CalVer tags
(2026.9.17.1) and keeps no long-lived release branch, so there is nothing for
the existing `release`-branch topology to watch. This adds an optional
source_repo_tag_regex: when set, the source resource versions on tags
(version_type=tags) instead of on spec changes landing on a branch.

Two things to know about that switch, both in the docstring as well. The git
resource consults `paths` only when versioning on commits, so under a tag
regex every matching tag rebuilds the client whether or not a spec moved --
the "only when the interface changed" property comes from the release cadence
instead of the path filter. `branch` stops applying too, so a matching tag
anywhere in the repo triggers it. The regex is anchored and rejects
hotfix/<sha>, v-prefixed and releases/-prefixed forms; checked against all
eight tags the repo currently has.

The LoadVarStep fix is not tag-specific. It read
.git/refs/heads/<branch>, which does not exist under a detached tag checkout.
.git/ref is written for every version_type and is already what this repo does
elsewhere (k8s_apps/pipeline.py, simple_pulumi/pipeline.py). The mitxonline
and mit-learn pipelines resolve it to the same SHA they read before;
regenerating both shows an unchanged `source` and only this file path moving.

Requires the version_type support added in ol-concourse. Without it the
argument falls through git_repo's **kwargs onto the Resource as a top-level
key, leaving it out of `source` entirely and the tag_regex inert -- it
generates clean and silently versions on commits, so this cannot merge until
that release lands.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CH3LZ41bD4ZKDnTQcviqb6
ol-analytics-api publishes two specs, one per tenant, and they are
independent APIs: an org-manager dashboard behind a user JWT, and a
machine-to-machine partner integration. A consumer of one has no reason to
pull the other's types in, so they ship as separate packages.

client_repo_subpath now takes a list as well as a string, and the publish job
emits one task per entry. The packages share the repo-root VERSION the bump
step writes, so a release moves them together.

Existing variants pass a string and are normalized to a single-element list,
so mitxonline and mit-learn still emit exactly one publish task against the
same directory as before. Their task name changes from `publish-node` to
`publish-node-<package>`, since two tasks in one plan cannot share a name;
that is a step label in the Concourse UI, not behavior.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CH3LZ41bD4ZKDnTQcviqb6
…e lib

Two problems with the tag support in the preceding commits, both found in
review.

The regex used `\d`. The resource filters tags with `grep -E`, which is POSIX
ERE, not PCRE: GNU grep emits "stray \ before d", drops the escape, and the
pattern then requires literal `d` characters. It matched none of the eight
tags ol-analytics-api has, so the pipeline would have set up clean and never
fired on a release. My earlier check of this pattern used Python's `re`, which
is the wrong engine and matched all eight. Verified the replacement by piping
every real tag through /usr/bin/grep -E, plus hotfix/<sha>, v- and
releases/-prefixed forms, a three-component version and a -rc1 suffix, none of
which match.

The ordering hazard also had no mechanical guard. An ol-concourse without
version_type support does not reject the argument -- git_repo forwards it
through **kwargs onto the Resource, so it lands as a top-level key, `source`
never gets it, and the resource falls back to versioning commits on `main`
with no paths filter, republishing the client on every commit. Merging this
before the ol-concourse release, or merging it and forgetting to relock, was a
silent failure. Generation now raises if version_type did not reach the
source.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CH3LZ41bD4ZKDnTQcviqb6
@blarghmatey
blarghmatey marked this pull request as ready for review September 23, 2026 14:13
Copilot AI balanced review requested due to automatic review settings September 23, 2026 14:13

Copilot AI left a comment

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.

Copilot review overview

🟡 Changes recommended

Sequential package publishing cannot recover from a partial npm publication failure, and the required ol-concourse release remains unavailable.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Adds tag-triggered TypeScript client generation for ol-analytics-api while supporting multiple packages per client repository.

Changes:

  • Adds the ol-analytics-api CalVer-tag configuration.
  • Supports tag-versioned sources and multiple publish subpaths.
  • Uses .git/ref for both branch and detached-tag checkouts.
File Description
configuration.py Configures two ol-analytics-api client packages.
api_clients_pipeline.py Adds tag handling and multi-package publishing.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/ol_concourse/pipelines/libraries/api_clients_pipeline.py Outdated
Sequential publish steps in one job can't recover from a partial
failure: if the first `yarn npm publish` succeeds and a later package
fails, retrying the job re-runs the first publish, which npm rejects
since that name/version already exists. Split the publish job per
subpath, each gated on the same generate-clients run, so a failed
package retries on its own without touching packages that already
published.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps9LGhZZH9g4D4MLpsoNuC
@blarghmatey
blarghmatey merged commit 8a57e3a into main Sep 26, 2026
11 checks passed
@blarghmatey
blarghmatey deleted the api-clients-tags branch September 26, 2026 22:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants