-
Notifications
You must be signed in to change notification settings - Fork 0
feat(quoter-bot): add docker image, compose, and docker hub publish #123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
julien-devatom
wants to merge
39
commits into
main
Choose a base branch
from
market-bot-docker-deploy-ddc5d2
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
599dbba
feat(market-making): add docker image, compose, and docker hub publish
julien-devatom 8bde1da
refactor(market-making): publish docker image from ci instead of script
julien-devatom cc75810
feat(market-making): publish docker image on github release
julien-devatom 2734841
ci(checks): port morpho-apps tag-releases and claude release notes
julien-devatom 4256ee4
fix(market-making): persist /state volume and pin release target sha
julien-devatom 3028dbc
fix(market-making): address codex review round on release + docker flow
julien-devatom ece5579
fix(market-making): scope bump releases to market-making, ignore *.env
julien-devatom 28accb7
Merge origin/main: reconcile Railway deploy with docker hub distribution
julien-devatom 87fff59
fix(market-making): close release-origin race and version-sync gaps
julien-devatom fe7fda1
chore(market-making): align compose before main sync
prd-carapulse[bot] 84bfe29
Merge branch 'main' into market-bot-docker-deploy-ddc5d2
prd-carapulse[bot] cc55033
fix(market-making): resolve main compose conflict
prd-carapulse[bot] c7ce000
Merge origin/main: adapt docker distribution to the pnpm migration
julien-devatom 6dd52c6
Merge remote branch: unify parallel main-sync resolutions
julien-devatom cccb69d
fix(repo): restore pnpm-era root manifest clobbered by bun tooling
julien-devatom af2f630
fix(repo): publicly hoist all dependencies for bun test resolution
julien-devatom 66ef2de
fix(repo): drop bun-test manifest clobber from previous commit
julien-devatom da9e62b
fix(market-making): align container release operations
prd-carapulse[bot] 5d6f176
ci(checks): add temporary hoist-layout diagnostics
julien-devatom 79c2a92
Merge remote-tracking branch 'origin/market-bot-docker-deploy-ddc5d2'…
julien-devatom 7d4aeae
fix(repo): stop bun test from mutating the pnpm dependency tree
julien-devatom 49b7ee4
fix(market-making): gate railway deploy on release preflight
julien-devatom 8f8426d
fix(repo): route workspace task filtering through pnpm
julien-devatom 4fa977e
test(market-making): align pages-workflow contract with pnpm filtering
julien-devatom 3dc5c87
fix(market-making): harden release tagging and pages workflow toolchain
julien-devatom cad3bfd
fix(market-making): resolve main integration conflicts
prd-carapulse[bot] fd2d8dd
chore(repo): sync current main (1/3)
prd-carapulse[bot] 1a616c8
chore(repo): sync current main (2/3)
prd-carapulse[bot] 0582ddb
chore(repo): sync current main (3/3)
prd-carapulse[bot] 6fe720b
fix(market-making): build runnable release image
prd-carapulse[bot] 4c5201d
fix(ci): address release review feedback
prd-carapulse[bot] 13e950c
chore: align Dockerfile before base merge
prd-carapulse[bot] 06377c5
Merge branch 'main' into market-bot-docker-deploy-ddc5d2
prd-carapulse[bot] 55ad6e9
fix(docker): disable Husky in market-making image
prd-carapulse[bot] d1b043f
fix(market-making): address release review feedback
prd-carapulse[bot] 8fe10f2
fix(market-making): address release review feedback
prd-carapulse[bot] c52b802
fix(market-making): address release review follow-ups
prd-carapulse[bot] 344c531
Merge origin/main: adopt the quoter-bot rename across the release sur…
julien-devatom e452ba9
Merge origin/main: adopt the Railway privilege-boundary hardening
julien-devatom File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| name: Claude write release notes | ||
|
|
||
| # Ported from morpho-apps: rewrites the GitHub-generated notes of freshly created bot releases with | ||
| # a Claude-authored summary, following the repo command .claude/commands/ci-write-release-notes.md. | ||
| # Triggered by the repository_dispatch that tag-releases.yml sends after creating releases. Manual | ||
| # re-run for a tag: | ||
| # gh api repos/morpho-org/morpho-bots/dispatches --method POST \ | ||
| # --field event_type=write-release-notes \ | ||
| # --field 'client_payload[release_tags]=quoter-bot-2026.08.04-1' | ||
| # | ||
| # The Claude step is skipped — not failed — while ANTHROPIC_API_KEY is not configured, so releasing | ||
| # keeps working before that secret exists; the GitHub-generated notes simply remain. Slack: | ||
| # release-slack-notify.yml already announced the release at publish time; to re-announce the | ||
| # rewritten notes, run that workflow manually with its `tag` input. (morpho-apps instead posts to | ||
| # per-app Slack channels from this workflow; this repo's single release channel makes that | ||
| # redundant.) | ||
|
|
||
| on: | ||
| repository_dispatch: | ||
| types: [write-release-notes] | ||
|
|
||
| jobs: | ||
| write-release-notes: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: write | ||
| id-token: write | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Fetch all tags | ||
| run: git fetch --tags --prune --force | ||
|
|
||
| # The secrets context is unavailable in job/step `if` expressions, so presence is probed in a | ||
| # step and exported as an output. | ||
| - name: Check Claude credentials | ||
| id: credentials | ||
| env: | ||
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | ||
| run: | | ||
| set -euo pipefail | ||
| if [ -n "$ANTHROPIC_API_KEY" ]; then | ||
| echo "available=true" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "available=false" >> "$GITHUB_OUTPUT" | ||
| echo "ANTHROPIC_API_KEY is not configured; keeping the GitHub-generated release notes." | ||
| fi | ||
|
|
||
| - name: Write with Claude | ||
| if: steps.credentials.outputs.available == 'true' | ||
| uses: anthropics/claude-code-action@657fb7c9c986158a19624b357bcbc8c6deb83598 # v1 | ||
| with: | ||
| anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} | ||
| github_token: ${{ secrets.GITHUB_TOKEN }} | ||
| # show_full_output stays off (unlike the morpho-apps original): the action's own docs | ||
| # reserve it for debugging in non-sensitive environments, and this job holds an API key | ||
| # and a write token while allowing Bash — a full transcript could retain | ||
| # credential-bearing tool output in the Actions logs. | ||
| prompt: /ci-write-release-notes | ||
| allowed_bots: 'github-actions[bot]' | ||
| claude_args: | | ||
| --allowedTools Bash,Read,Glob,Grep | ||
| env: | ||
| # description: "Space-separated list of release tags to update" | ||
| RELEASE_TAGS: ${{ github.event.client_payload.release_tags }} |
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| name: Deploy quoter-bot | ||
|
|
||
| # Publishes the quoter-bot bot image to Docker Hub when a `quoter-bot-*` GitHub release is | ||
| # published (repo CalVer convention: `quoter-bot-YYYY.MM.DD-N`), or on manual dispatch. Unlike | ||
| # the Railway bots (deploy-bot.yml), "deploy" here means publish only: operators pull and run the | ||
| # image themselves (see bots/quoter-bot/README.md), so there is no service to restart. The Slack | ||
| # announcement comes AFTER a successful publish: release-slack-notify.yml skips quoter-bot | ||
| # release events and the final step here re-enters it through its manual `tag` input once every | ||
| # image tag is pushed — so an announced release always has its image. | ||
| # | ||
| # Releases come from tag-releases.yml (a merged PR bumping the bot's package.json version to a new | ||
| # CalVer value) or from deploy-production.yml's Release-quoter-bot job after a successful | ||
| # `release-quoter-bot`-labeled Railway deploy. Both create the release with a GitHub App | ||
| # installation token precisely so this workflow fires — GitHub never runs workflows for events | ||
| # raised with the default GITHUB_TOKEN. A user-created release (`gh release create` or the releases | ||
| # UI) triggers identically. workflow_dispatch stays as the escape hatch for re-publishing, e.g. | ||
| # `gh workflow run deploy-quoter-bot.yml -f tag=latest`. | ||
| # | ||
| # A release publish builds the tagged commit and pushes `<release tag>` plus `git-<shortsha>`. The | ||
| # highest non-prerelease CalVer release also moves `latest`; backfilled older releases and | ||
| # prereleases never do. A dispatch builds the dispatched ref and pushes the `tag` input (default | ||
| # `latest`) plus `git-<shortsha>`. | ||
| # | ||
| # Credentials live in the `quoter-bot-dockerhub` GitHub Environment (distinct from | ||
| # `quoter-bot-production`, which holds the Railway deploy credentials): secrets | ||
| # DOCKERHUB_USERNAME and DOCKERHUB_TOKEN (a Docker Hub access token, write scope) plus variable | ||
| # DOCKERHUB_REPOSITORY (`<namespace>/<name>`, e.g. `morphoorg/quoter-bot-bot`). In the | ||
| # environment's deployment branches/tags policy allow branch `main` AND tags matching | ||
| # `quoter-bot-*` — release runs execute on the tag ref, so a branch-only policy rejects them. | ||
|
|
||
| on: | ||
| release: | ||
| types: [published] | ||
| workflow_dispatch: | ||
| inputs: | ||
| tag: | ||
| description: Movable primary tag to publish alongside the immutable git-<sha> tag | ||
| required: false | ||
| type: string | ||
| default: latest | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| # Serialize publishes so two runs can't interleave their pushes of the movable tag. | ||
| group: deploy-quoter-bot | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| Publish: | ||
| # Releases are repo-wide (the Railway bots cut `blue-liq-*` etc.); only quoter-bot tags | ||
| # concern this image. Other releases simply skip this job. | ||
| if: ${{ github.event_name == 'workflow_dispatch' || startsWith(github.event.release.tag_name, 'quoter-bot-') }} | ||
| runs-on: ubuntu-latest | ||
| environment: quoter-bot-dockerhub | ||
| permissions: | ||
| contents: read | ||
| # `gh workflow run` for the post-publish Slack announcement. | ||
| actions: write | ||
| steps: | ||
| # On a release event this checks out the tagged commit (github.sha is the tag's commit), so | ||
| # the image is built from exactly the released tree, not main HEAD. | ||
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | ||
|
|
||
| # The token reaches docker via stdin only — never argv, never a workflow-file literal. | ||
| - name: Login to Docker Hub | ||
| env: | ||
| DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} | ||
| DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} | ||
| run: | | ||
| set -euo pipefail | ||
| : "${DOCKERHUB_USERNAME:?set secret DOCKERHUB_USERNAME on the quoter-bot-dockerhub environment}" | ||
| : "${DOCKERHUB_TOKEN:?set secret DOCKERHUB_TOKEN on the quoter-bot-dockerhub environment}" | ||
| printf '%s' "$DOCKERHUB_TOKEN" | docker login --username "$DOCKERHUB_USERNAME" --password-stdin | ||
|
|
||
| # The build context is the repo root so the pnpm workspace (packages/*) resolves — see | ||
| # bots/quoter-bot/Dockerfile. The docker release tag is the git tag verbatim, so the | ||
| # image, git tag, and GitHub release cross-reference with zero transformation. | ||
| - name: Build and push | ||
| env: | ||
| REPOSITORY: ${{ vars.DOCKERHUB_REPOSITORY }} | ||
| EVENT: ${{ github.event_name }} | ||
| RELEASE_TAG: ${{ github.event.release.tag_name }} | ||
| PRERELEASE: ${{ github.event.release.prerelease }} | ||
| DISPATCH_TAG: ${{ inputs.tag || 'latest' }} | ||
| SHA: ${{ github.sha }} | ||
| GH_TOKEN: ${{ github.token }} | ||
| run: | | ||
| set -euo pipefail | ||
| : "${REPOSITORY:?set variable DOCKERHUB_REPOSITORY on the quoter-bot-dockerhub environment}" | ||
| # Docker Hub `<namespace>/<name>` only. A dotted (or `localhost`) first component would be | ||
| # read by docker as a registry host and silently push somewhere other than Docker Hub. | ||
| echo "$REPOSITORY" | grep -Eq '^[a-z0-9]+((_|__|-+)[a-z0-9]+)*/[a-z0-9]+(([._]|__|-+)[a-z0-9]+)*$' \ | ||
| || { echo "DOCKERHUB_REPOSITORY must be a lowercase Docker Hub <namespace>/<name> repository" >&2; exit 1; } | ||
| if [ "$EVENT" = "release" ]; then | ||
| CALVER_PATTERN="^[0-9]{4}\.[0-9]{2}\.[0-9]{2}-[1-9][0-9]*$" | ||
| # The release tag must equal quoter-bot-<package version> of the tagged commit: | ||
| # a mistyped or version-skewed release would otherwise publish an image (and move | ||
| # `latest`) whose tag disagrees with `mm --version` inside it. | ||
| package_version="$(node -p "require('./bots/quoter-bot/package.json').version")" | ||
| if ! [[ "$package_version" =~ $CALVER_PATTERN ]]; then | ||
| echo "package version $package_version is not CalVer (expected YYYY.MM.DD-N) — refusing to publish release image tags" >&2 | ||
| exit 1 | ||
| fi | ||
| expected="quoter-bot-${package_version}" | ||
| if [ "$RELEASE_TAG" != "$expected" ]; then | ||
| echo "release tag $RELEASE_TAG does not match the tagged commit's package version ($expected) — delete the release and cut it from a matching version bump" >&2 | ||
| exit 1 | ||
| fi | ||
| tags=("$RELEASE_TAG") | ||
| # `latest` tracks the highest stable CalVer release, not the most recently published | ||
| # release. This prevents a later backfill of an older release from downgrading it. | ||
| if [ "$PRERELEASE" != "true" ]; then | ||
| highest_stable_tag="$( | ||
| gh api --paginate "repos/$GITHUB_REPOSITORY/releases?per_page=100" \ | ||
| --jq '.[] | select(.draft == false and .prerelease == false) | .tag_name' \ | ||
| | grep -E '^quoter-bot-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-[1-9][0-9]*$' \ | ||
| | sort -V \ | ||
| | tail -n 1 | ||
| )" | ||
| if [ "$RELEASE_TAG" = "$highest_stable_tag" ]; then | ||
| tags+=("latest") | ||
| else | ||
| echo "Not moving latest: $highest_stable_tag is newer than backfilled release $RELEASE_TAG" | ||
| fi | ||
| fi | ||
| else | ||
| if [[ "$DISPATCH_TAG" == quoter-bot-* || "$DISPATCH_TAG" == git-* ]]; then | ||
| echo "refusing to overwrite immutable release or commit tag $DISPATCH_TAG from a manual dispatch" >&2 | ||
| exit 1 | ||
| fi | ||
| tags=("$DISPATCH_TAG") | ||
| fi | ||
|
prd-carapulse[bot] marked this conversation as resolved.
|
||
| tags+=("git-${SHA:0:7}") | ||
| build_args=() | ||
| for tag in "${tags[@]}"; do | ||
| echo "$tag" | grep -Eq '^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$' \ | ||
| || { echo "invalid docker tag: $tag" >&2; exit 1; } | ||
| build_args+=(--tag "$REPOSITORY:$tag") | ||
| done | ||
| echo "Building ${tags[*]} from $SHA…" | ||
| docker build --file bots/quoter-bot/Dockerfile.release "${build_args[@]}" . | ||
| for tag in "${tags[@]}"; do | ||
| docker push "$REPOSITORY:$tag" | ||
| echo "Published docker.io/$REPOSITORY:$tag" | ||
| done | ||
|
|
||
| # Announce only now that every image tag exists. release-slack-notify.yml deliberately skips | ||
| # quoter-bot release events; its manual `tag` input re-enters it here. workflow_dispatch | ||
| # fired with the default GITHUB_TOKEN does start workflows (unlike tag/release events). | ||
| - name: Announce release on Slack | ||
| if: github.event_name == 'release' | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| RELEASE_TAG: ${{ github.event.release.tag_name }} | ||
| run: gh workflow run release-slack-notify.yml -f tag="$RELEASE_TAG" | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.