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
18 changes: 5 additions & 13 deletions .github/workflows/lint-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
id: setup-node
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
node-version-file: 'package.json'
cache: 'yarn'
- run: yarn install

Expand All @@ -40,7 +40,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
node-version-file: 'package.json'
cache: 'yarn'

- run: yarn install
Expand All @@ -59,7 +59,7 @@ jobs:
id: setup-node
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
node-version-file: 'package.json'
cache: 'yarn'
- run: yarn install
- run: yarn test
Expand All @@ -76,7 +76,7 @@ jobs:
id: setup-node
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
node-version-file: 'package.json'
cache: 'yarn'

- name: Cache Package Build
Expand All @@ -97,10 +97,7 @@ jobs:
- build-e2e-package
strategy:
matrix:
node-version: [14, 16, 18, 20]
include:
- node-version: 14
npm-version: 7
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v3

Expand All @@ -118,11 +115,6 @@ jobs:
path: ${{ env.TARBALL_PATH }}
key: ${{ runner.os }}-node-${{ needs.prepare-node.outputs.build-node-version }}-${{ env.GITHUB_SHA }}

# Some versions of Node (like Node 14) ships with a version of NPM that does not work for us
# so we need to install a specific version
- name: Optionally update NPM if needed
if: ${{ matrix.npm-version }}
run: npm i -g npm@${{ matrix.npm-version }}
# Just make sure it installs correctly for now and we'll call it good. No testing.
- run: yarn test-e2e:install

121 changes: 121 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: Publish to npm

on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+*'

concurrency:
group: publish
cancel-in-progress: false

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
env:
GH_TOKEN: ${{ secrets.PUBLISH_PAT }}
steps:
- name: Resolve token
id: token
run: echo "value=${GH_TOKEN:-${{ github.token }}}" >> $GITHUB_OUTPUT

- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: main
token: ${{ steps.token.outputs.value }}

- name: Extract version from tag
id: tag
run: echo "version=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT

- name: Verify tag is on main branch
run: |
if ! git branch -r --contains ${{ github.ref_name }} | grep -q 'origin/main'; then
echo "::error::Tag ${{ github.ref_name }} is not on the main branch. Tags must be created from main."
exit 1
fi

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
registry-url: 'https://registry.npmjs.org'
cache: yarn

- name: Verify tag matches package.json version
run: |
MANIFEST_VERSION=$(node -p 'require("./package.json").version')
TAG_VERSION=${{ steps.tag.outputs.version }}
if [ "$MANIFEST_VERSION" != "$TAG_VERSION" ]; then
echo "::error::Tag version ($TAG_VERSION) does not match package.json version ($MANIFEST_VERSION). Bump the version in package.json before tagging."
exit 1
fi

- name: Check for pre-release version
id: prerelease
run: |
VERSION=${{ steps.tag.outputs.version }}
if echo "$VERSION" | grep -qE '[-]'; then
echo "is_prerelease=true" >> $GITHUB_OUTPUT
echo "npm_tag=next" >> $GITHUB_OUTPUT
else
echo "is_prerelease=false" >> $GITHUB_OUTPUT
echo "npm_tag=latest" >> $GITHUB_OUTPUT
fi

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Build
run: yarn prepare

- name: Run tests
run: yarn test

- name: Validate package
run: npm pack --dry-run

- name: Publish to npm
run: npm publish --access public --tag ${{ steps.prerelease.outputs.npm_tag }} --provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Install Claude Code
run: npm install -g @anthropic-ai/claude-code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

- name: Generate changelog entry
run: node scripts/generate-changelog.js ${{ steps.tag.outputs.version }}
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

- name: Commit changelog
uses: stefanzweifel/git-auto-commit-action@v4
with:
branch: main
commit_message: "docs: update CHANGELOG.md for v${{ steps.tag.outputs.version }}"
file_pattern: CHANGELOG.md
token: ${{ steps.token.outputs.value }}

- name: Extract changelog for release notes
id: changelog
run: |
VERSION=${{ steps.tag.outputs.version }}
NOTES=$(sed -n "/## \[${VERSION}\]/,/## \[/{ /## \[${VERSION}\]/d; /## \[/d; p; }" CHANGELOG.md)
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "notes<<$EOF" >> $GITHUB_OUTPUT
echo "$NOTES" >> $GITHUB_OUTPUT
echo "$EOF" >> $GITHUB_OUTPUT

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
name: v${{ steps.tag.outputs.version }}
body: ${{ steps.changelog.outputs.notes }}
prerelease: ${{ steps.prerelease.outputs.is_prerelease }}
token: ${{ steps.token.outputs.value }}
1 change: 0 additions & 1 deletion .nvmrc

This file was deleted.

15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- Unit tests for OAuth/Bearer auth header encoding, including precedence when both `apiKey` and `accessToken` are provided.
- Unit tests for `baseURL` constructor configuration.
- Unit tests for version constants (`VERSION_LATEST`, `VERSION_LATEST_PUBLISHED`).
- Unit tests verifying API key trailing colon encoding and OAuth token encoding.
- GitHub Actions publish workflow — publishes to npm on merge to main.
- Integration test script at `scripts/integration-test.js`.
- `CONTRIBUTING.md` with development setup, testing, CI/CD, and publishing documentation.

### Changed
- README updated with OAuth auth documentation and version constants reference.
- `baseURL` documented as an advanced constructor option.

## [v3.3.2]
- Update the default `createEtchPacket` mutation to support `allowUpdates`[`#502`](https://github.com/anvilco/node-anvil/pull/502)

Expand Down
117 changes: 117 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Contributing to Anvil Node.js Client

## Development Setup

Install dependencies:

```sh
yarn install
```

## Running Tests

```sh
yarn test

# Watch mode
yarn test:watch
```

## Building

Building with babel will output in the `/lib` directory:

```sh
yarn build
```

## Integration Testing

The integration test script (`scripts/integration-test.js`) runs a suite of tests against a live Anvil environment. By default it targets the staging environment.

### Running Integration Tests

```sh
ANVIL_API_KEY=your_key node scripts/integration-test.js
```

You can target a different environment by setting `ANVIL_BASE_URL`:

```sh
ANVIL_API_KEY=your_key ANVIL_BASE_URL=https://staging.useanvil.com node scripts/integration-test.js
```

The default base URL is `https://staging.useanvil.com`.

### baseURL Configuration for Staging

When working with the staging environment, configure the client with:

```js
const client = new Anvil({
apiKey: 'your-api-key',
baseURL: 'https://staging.useanvil.com',
})
```

## CI/CD

**PR Workflow:** Runs lint and tests on every pull request.

**Publish Workflow:** Publishes to npm on merge to main.

### Required GitHub Secrets

| Secret | Description |
|--------|-------------|
| `NPM_TOKEN` | npm authentication token for publishing |
| `ANTHROPIC_API_KEY` | Anthropic API key for changelog generation |
| `PUBLISH_PAT` | Personal access token with `contents: write` — needed to push the changelog commit and create GitHub Releases when branch protection is enabled. Falls back to `GITHUB_TOKEN` if not set (will fail if branch protection requires PR reviews). |

## Changelog Generation

The publish workflow automatically generates a changelog entry before publishing.
You can also run it manually:

```sh
node scripts/generate-changelog.js <version>
```

The script:
- If `CHANGELOG.md` has an `[Unreleased]` section with content, it stamps it
with the given version and today's date.
- If there's no `[Unreleased]` content, it reads the git log since the last
tag and uses Claude to generate an entry.
- A fresh empty `[Unreleased]` section is added above the new entry.

## Publishing

Publishing is tag-based. Merging to `main` does not publish — only pushing a
version tag triggers a release.

1. Bump the version in `package.json` in a PR and merge to `main`.
2. Create and push a tag **from the main branch** matching the version:
```sh
git checkout main && git pull
git tag v3.4.0
git push origin v3.4.0
```
3. The publish workflow will:
- Verify the tag is on the `main` branch (rejects tags on other branches)
- Verify the tag matches `package.json` (fails if they differ)
- Run lint and tests
- Generate a changelog entry and auto-commit it to `main`
- Validate the package with `npm pack --dry-run`
- Publish to npm with `--provenance` for supply chain attestation
- Create a GitHub Release with the changelog as release notes

### Pre-release versions

Tags like `v1.2.3-alpha.1` or `v1.2.3-beta.0` are detected as pre-releases.
They publish to npm under the `next` tag (instead of `latest`) and the GitHub
Release is marked as a pre-release.

### Tag format

Only tags matching `v<major>.<minor>.<patch>` trigger the workflow (e.g.,
`v3.4.0`, `v3.4.0-alpha.1`). Tags like `latest` or `release` are ignored.
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,10 +388,28 @@ Options for the Anvil Client. Defaults are shown after each option key.

```js
{
apiKey: <your_api_key>, // Required. Your API key from your Anvil organization settings
apiKey: <your_api_key>, // Your API key from your Anvil organization settings
accessToken: <oauth_token>, // OR an OAuth access token (Bearer auth)
baseURL: 'https://app.useanvil.com', // Advanced: override the base URL for API requests
}
```

At least one of `apiKey` or `accessToken` is required. If both are provided, `accessToken` takes precedence.

**API Key Auth:**
```js
const client = new Anvil({ apiKey: 'your-api-key' })
```

**OAuth Auth:**
```js
const client = new Anvil({ accessToken: 'your-oauth-token' })
```

**Version Constants:**
- `Anvil.VERSION_LATEST` (`-1`) — use the latest version (including drafts)
- `Anvil.VERSION_LATEST_PUBLISHED` (`-2`) — use the latest published version (default)

### Rate Limits

Our API has request rate limits in place. The initial request made by this client will parse the limits for your account from the response headers, and then handle the throttling of subsequent requests for you automatically. In the event that this client still receives a `429 Too Many Requests` error response, it will wait the specified duration then retry the request. The client attempts to avoid `429` errors by throttling requests after the number of requests within the specified time period has been reached.
Expand Down Expand Up @@ -431,3 +449,4 @@ yarn test
# Watches the `src` and `test` directories
yarn test:watch
```

2 changes: 1 addition & 1 deletion babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module.exports = {
// modules: 'commonjs',
targets: {
// Keep this roughly in-line with our "engines.node" value in package.json
node: '14',
node: '18',
},
exclude: [
// Node 14+ supports this natively AND we need it to operate natively
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@
"url": "git+https://github.com/anvilco/node-anvil.git"
},
"engines": {
"node": ">=14"
"node": ">=18"
},
"volta": {
"node": "22.22.0",
"yarn": "1.22.22"
},
"publishConfig": {
"registry": "https://registry.npmjs.org",
Expand Down
1 change: 1 addition & 0 deletions scripts/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ANVIL_API_KEY=iZBskHleNzJejIf835JfEwl1ypfvusF2
Loading
Loading