Skip to content

Commit 9e4a755

Browse files
authored
feat: autopublish to npm registry in GH (#539)
* feat: autopublish to npm registry in GH * chore: skip test * chore: disable test * chore: create pre-release * chore: always authenticate * chore: try to add the auth token * Prevent external projects to publish * chore: debug auth * Add contributing guide * chore: test * test: test * chore: test 3 * Allow to create a comment * chore: use the right versions * chore: update comment * chore: test * chore: use hash * chore: test * chore: cleanup * chore: sort and remove duplicates * chore: allow auto-publish on main branches * chore: cleanup * chore: publish as public * fic: fix broken script * docs: simplify readme * chore: add last update time * chore: fix * chore: give more instructions on how to add the GH token * chore: test alternative to adding the registry in .npmrc
1 parent aa0f90c commit 9e4a755

File tree

5 files changed

+344
-14
lines changed

5 files changed

+344
-14
lines changed
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
name: Publish to GitHub Packages
2+
3+
on:
4+
# Trigger on pull requests (for testing/validation) - only from same repo
5+
pull_request:
6+
types: [opened, synchronize, reopened]
7+
# Trigger on pushes to main branch
8+
push:
9+
branches:
10+
- main
11+
# Allow manual triggering
12+
workflow_dispatch:
13+
14+
env:
15+
NODE_VERSION: lts/jod
16+
REGISTRY: npm.pkg.github.com
17+
SCOPE: '@cowprotocol'
18+
19+
permissions:
20+
contents: read
21+
packages: write
22+
issues: write
23+
pull-requests: write
24+
25+
jobs:
26+
publish:
27+
name: Publish to GitHub Packages
28+
runs-on: ubuntu-latest
29+
30+
# Security: Only run on internal PRs, main branch pushes, or external PRs with explicit approval
31+
if: github.event_name == 'push' || (github.event_name == 'pull_request' && (github.event.pull_request.head.repo.full_name == github.repository || contains(github.event.pull_request.labels.*.name, 'allow-publish')))
32+
33+
steps:
34+
- name: Security Check
35+
run: |
36+
if [ "${{ github.event_name }}" = "pull_request" ]; then
37+
echo "🔒 Checking PR security..."
38+
echo "PR from: ${{ github.event.pull_request.head.repo.full_name }}"
39+
echo "Target repo: ${{ github.repository }}"
40+
41+
# Check if it's an internal PR
42+
if [ "${{ github.event.pull_request.head.repo.full_name }}" = "${{ github.repository }}" ]; then
43+
echo "✅ Internal PR - proceeding with publish"
44+
else
45+
# Check for allow-publish label
46+
echo "🔍 Checking for 'allow-publish' label..."
47+
if echo "${{ toJson(github.event.pull_request.labels) }}" | grep -q '"name":"allow-publish"'; then
48+
echo "✅ External PR with 'allow-publish' label - proceeding with publish"
49+
else
50+
echo "❌ External PR without 'allow-publish' label - skipping publish for security"
51+
echo "💡 To enable publishing for this external PR, add the 'allow-publish' label"
52+
exit 1
53+
fi
54+
fi
55+
else
56+
echo "✅ Push to main branch - proceeding with publish"
57+
fi
58+
59+
- name: Cancel Previous Runs
60+
uses: styfle/[email protected]
61+
with:
62+
access_token: ${{ github.token }}
63+
64+
- name: Remove broken apt repos [Ubuntu]
65+
run: |
66+
for apt_file in `grep -lr microsoft /etc/apt/sources.list.d/`; do sudo rm $apt_file; done
67+
68+
- name: Checkout code
69+
uses: actions/checkout@v4
70+
71+
- name: Setup Node.js
72+
uses: actions/setup-node@v4
73+
with:
74+
node-version: ${{ env.NODE_VERSION }}
75+
scope: ${{ env.SCOPE }}
76+
always-auth: true
77+
78+
- name: Setup pnpm
79+
uses: pnpm/action-setup@v4
80+
with:
81+
version: 10.8.0
82+
83+
- name: Cache pnpm store
84+
uses: actions/cache@v4
85+
with:
86+
path: ~/.pnpm-store
87+
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
88+
89+
- name: Install dependencies
90+
run: pnpm install --frozen-lockfile
91+
92+
- name: Generate code
93+
run: pnpm codegen
94+
95+
- name: Build packages
96+
run: pnpm build
97+
98+
- name: Run tests
99+
run: pnpm test
100+
101+
- name: Run linting
102+
run: pnpm lint
103+
104+
- name: Configure npm for GitHub Packages
105+
run: |
106+
# Override registry for @cowprotocol scope to use GitHub Packages
107+
echo "@cowprotocol:registry=https://${{ env.REGISTRY }}" >> ~/.npmrc
108+
109+
# Always authenticate
110+
echo "always-auth=true" >> ~/.npmrc
111+
112+
# Set auth token
113+
echo "//${{ env.REGISTRY }}/:_authToken=${{ secrets.GITHUB_TOKEN }}" >> ~/.npmrc
114+
# Keep default registry as npmjs.org for other packages
115+
echo "registry=https://registry.npmjs.org/" >> ~/.npmrc
116+
117+
- name: Determine version strategy
118+
id: version
119+
run: |
120+
COMMIT_HASH=$(echo "${{ github.sha }}" | cut -c1-8)
121+
if [ "${{ github.event_name }}" = "pull_request" ]; then
122+
# For PRs, use PR number + commit hash for uniqueness
123+
VERSION_SUFFIX="pr-${{ github.event.number }}-$COMMIT_HASH"
124+
echo "version_suffix=$VERSION_SUFFIX" >> $GITHUB_OUTPUT
125+
echo "tag=pr" >> $GITHUB_OUTPUT
126+
echo "is_pr=true" >> $GITHUB_OUTPUT
127+
else
128+
# For main branch, use latest tag
129+
BRANCH_NAME=$(echo "${{ github.ref }}" | sed 's/refs\/heads\///')
130+
VERSION_SUFFIX="${BRANCH_NAME}-$COMMIT_HASH"
131+
echo "version_suffix=$VERSION_SUFFIX" >> $GITHUB_OUTPUT
132+
echo "tag=latest" >> $GITHUB_OUTPUT
133+
echo "is_pr=false" >> $GITHUB_OUTPUT
134+
fi
135+
136+
- name: Create pre-release versions for PRs
137+
if: steps.version.outputs.is_pr == 'true'
138+
run: |
139+
# Create pre-release versions for all packages
140+
find packages -name "package.json" -exec sh -c '
141+
for file; do
142+
echo "Creating pre-release version for $file"
143+
# Use npm version to create pre-release version
144+
cd "$(dirname "$file")"
145+
pnpm version prerelease --preid="${{ steps.version.outputs.version_suffix }}" --no-git-tag-version
146+
cd - > /dev/null
147+
done
148+
' _ {} \;
149+
150+
- name: Publish packages to GitHub Packages
151+
run: |
152+
# Publish all packages and capture versions
153+
echo "Publishing packages..."
154+
pnpm publish -r --filter="./packages/**" --tag ${{ steps.version.outputs.tag }} --no-git-checks --access public > publish_output.txt 2>&1
155+
156+
# Show the actual publish output for debugging
157+
echo "=== Full publish output ==="
158+
cat publish_output.txt
159+
160+
# Extract published versions and remove duplicates
161+
echo "=== Extracting published versions ==="
162+
grep -o '@cowprotocol/[^@]*@[0-9][^[:space:]]*' publish_output.txt | sort -u > published_versions.txt
163+
env:
164+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
165+
166+
- name: Restore original versions (PR only)
167+
if: steps.version.outputs.is_pr == 'true'
168+
run: |
169+
# Restore original package.json versions
170+
git checkout -- packages/*/package.json
171+
172+
- name: Update PR comment with package info
173+
if: steps.version.outputs.is_pr == 'true'
174+
uses: actions/github-script@v7
175+
with:
176+
script: |
177+
const prVersion = '${{ steps.version.outputs.version_suffix }}';
178+
const isExternalPR = '${{ github.event.pull_request.head.repo.full_name }}' !== '${{ github.repository }}';
179+
180+
// Read published versions from file
181+
const fs = require('fs');
182+
let publishedVersions = [];
183+
try {
184+
const versionsContent = fs.readFileSync('published_versions.txt', 'utf8');
185+
publishedVersions = versionsContent.trim().split('\n').filter(line => line.trim());
186+
} catch (error) {
187+
console.log('Could not read published versions file');
188+
process.exit(1);
189+
}
190+
191+
const now = new Date().toISOString();
192+
const timestamp = new Date(now).toLocaleString('en-US', {
193+
timeZone: 'UTC',
194+
year: 'numeric',
195+
month: 'short',
196+
day: 'numeric',
197+
hour: '2-digit',
198+
minute: '2-digit',
199+
second: '2-digit',
200+
timeZoneName: 'short'
201+
});
202+
203+
const commentBody = `## 📦 GitHub Packages Published
204+
**Last updated:** ${timestamp}
205+
206+
The following packages have been published to GitHub Packages with pre-release version \`${prVersion}\`:
207+
208+
${publishedVersions.map(pkg => `- \`${pkg}\``).join('\n')}
209+
210+
${isExternalPR ? '> **Note:** This is an external PR with the \`allow-publish\` label enabled.' : ''}
211+
212+
---
213+
214+
### Installation
215+
216+
These packages require authentication to install from GitHub Packages. First, configure your token:
217+
218+
\`\`\`bash
219+
# Set your GitHub token
220+
# 1. create one at https://github.com/settings/tokens. Make sure you select the option "Generate new token (classic)"
221+
# 2. Check only the "read:packages" scope
222+
# 3. Add the token to the npm config
223+
npm config set //${{ env.REGISTRY }}/:_authToken YOUR_GITHUB_TOKEN
224+
\`\`\`
225+
226+
Then install any of the packages above:
227+
\`\`\`bash
228+
# Yarn
229+
yarn add ${publishedVersions[0]} --registry https://${{ env.REGISTRY }}
230+
231+
# pnpm
232+
pnpm install ${publishedVersions[0]} --registry https://${{ env.REGISTRY }}
233+
234+
# NPM
235+
npm install ${publishedVersions[0]} --registry https://${{ env.REGISTRY }}
236+
\`\`\`
237+
238+
### View Packages
239+
240+
You can view the published packages at: https://github.com/cowprotocol/cow-sdk/packages`;
241+
242+
// Find existing comment from this workflow
243+
const comments = await github.rest.issues.listComments({
244+
owner: context.repo.owner,
245+
repo: context.repo.repo,
246+
issue_number: context.issue.number
247+
});
248+
249+
const existingComment = comments.data.find(comment =>
250+
comment.user.type === 'Bot' &&
251+
comment.body.includes('📦 GitHub Packages Published')
252+
);
253+
254+
if (existingComment) {
255+
// Update existing comment
256+
await github.rest.issues.updateComment({
257+
owner: context.repo.owner,
258+
repo: context.repo.repo,
259+
comment_id: existingComment.id,
260+
body: commentBody
261+
});
262+
console.log('Updated existing comment');
263+
} else {
264+
// Create new comment if none exists
265+
await github.rest.issues.createComment({
266+
owner: context.repo.owner,
267+
repo: context.repo.repo,
268+
issue_number: context.issue.number,
269+
body: commentBody
270+
});
271+
console.log('Created new comment');
272+
}

CONTRIBUTING.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,36 @@
11
# Contributing
22

3+
## Pull Request Guidelines
4+
5+
### 📏 **Keep PRs Small and Focused**
6+
7+
- **One feature per PR** - Each PR should focus on a single change or feature
8+
- **Break large features** into multiple smaller PRs for easier review
9+
- **Atomic changes** that can be reviewed and merged independently
10+
11+
### 📝 **Clear Descriptions**
12+
13+
- **Explain the changes** - What does your PR do and why?
14+
- **Provide context** - Why is this change needed?
15+
- **Include examples** - How should reviewers test your changes?
16+
- **Link related issues** - Reference any GitHub issues this PR addresses
17+
18+
### 🧪 **Testing Requirements**
19+
20+
- **New code must be covered** by meaningful unit tests
21+
- **Update tests** when modifying existing functionality
22+
- **Ensure all tests pass** before creating the PR
23+
24+
### 🧹 **Code Quality**
25+
26+
- **Remove dead code** - Delete unused functions, variables, and imports
27+
- **No debugging code** - Remove console.log, debugger statements, and temporary code
28+
- **Professional appearance** - Code should be clean, readable, and production-ready
29+
330
## Publishing to npm
431

532
In this project packages we use `workspace:*` as version for local packages in `package.json` dependencies.
633

7-
You must not manually publish with `npm` (like npm publish) — it wont rewrite `workspace:*` before publishing.
34+
You must not manually publish with `npm` (like npm publish) — it won't rewrite `workspace:*` before publishing.
835

936
**→ Always publish with `pnpm publish` (or `changeset publish`, or a release pipeline that uses pnpm).**

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
| ![Statements](https://img.shields.io/badge/statements-94.77%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-76.78%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-97.43%25-brightgreen.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-97.67%25-brightgreen.svg?style=flat) |
1010

1111
## Use CoW Protocol
12+
1213
> Check [**@cowprotocol/cow-sdk**](https://github.com/cowprotocol/cow-sdk/tree/main/packages/sdk/README.md) to learn how to trade on the CoW Protocol (get quote, verify amounts, sign and send order)
1314
1415
## Technical Overview
@@ -18,6 +19,7 @@ This is a **TypeScript monorepo** containing the complete CoW Protocol SDK ecosy
1819
### Monorepo Architecture
1920

2021
The project uses modern tooling for efficient development and publishing:
22+
2123
- **🏗️ Build System**: [Turbo](https://turbo.build/) for fast, incremental builds and task orchestration
2224
- **📦 Package Manager**: [pnpm](https://pnpm.io/) v10.8+ with workspaces for efficient dependency management
2325
- **🔧 TypeScript**: Shared TypeScript configuration across all packages
@@ -30,24 +32,29 @@ The project uses modern tooling for efficient development and publishing:
3032
### Package Structure
3133

3234
#### 🎯 Main SDK Package
35+
3336
- **[`@cowprotocol/cow-sdk`](https://github.com/cowprotocol/cow-sdk/tree/main/packages/sdk/README.md)** - Complete package that re-exports all other packages for easy consumption
3437

3538
#### 🔧 Core Trading Packages
39+
3640
- **[`@cowprotocol/sdk-trading`](https://github.com/cowprotocol/cow-sdk/tree/main/packages/trading/README.md)** - High-level trading SDK with built-in quote fetching, order signing, and posting
3741
- **[`@cowprotocol/sdk-order-book`](https://github.com/cowprotocol/cow-sdk/tree/main/packages/order-book/README.md)** - Order book API client for retrieving orders, trades, and posting orders
3842
- **[`@cowprotocol/sdk-order-signing`](https://github.com/cowprotocol/cow-sdk/tree/main/packages/order-signing/README.md)** - Cryptographic utilities for signing orders and cancellations
3943

4044
#### 🌉 Advanced Features
45+
4146
- **[`@cowprotocol/sdk-bridging`](https://github.com/cowprotocol/cow-sdk/tree/main/packages/bridging/README.md)** - Cross-chain token transfers and bridging functionality
4247
- **[`@cowprotocol/sdk-composable`](https://github.com/cowprotocol/cow-sdk/tree/main/packages/composable/README.md)** - Programmatic orders (TWAP, conditional orders, etc.)
4348
- **[`@cowprotocol/sdk-cow-shed`](https://github.com/cowprotocol/cow-sdk/tree/main/packages/cow-shed/README.md)** - Account abstraction with smart contract capabilities
4449

4550
#### 🔌 Provider Adapters
51+
4652
- **[`@cowprotocol/sdk-viem-adapter`](https://github.com/cowprotocol/cow-sdk/tree/main/packages/providers/viem-adapter/README.md)** - Viem blockchain library adapter
4753
- **[`@cowprotocol/sdk-ethers-v6-adapter`](https://github.com/cowprotocol/cow-sdk/tree/main/packages/providers/ethers-v6-adapter/README.md)** - Ethers.js v6 adapter
4854
- **[`@cowprotocol/sdk-ethers-v5-adapter`](https://github.com/cowprotocol/cow-sdk/tree/main/packages/providers/ethers-v5-adapter/README.md)** - Ethers.js v5 adapter
4955

5056
#### 📚 Supporting Packages
57+
5158
- **[`@cowprotocol/sdk-app-data`](https://github.com/cowprotocol/cow-sdk/tree/main/packages/app-data/README.md)** - AppData schema definitions and metadata handling
5259
- **[`@cowprotocol/sdk-config`](https://github.com/cowprotocol/cow-sdk/tree/main/packages/config/README.md)** - Configuration constants and chain settings
5360
- **[`@cowprotocol/sdk-common`](https://github.com/cowprotocol/cow-sdk/tree/main/packages/common/README.md)** - Common utilities, types, and shared functionality
@@ -56,6 +63,7 @@ The project uses modern tooling for efficient development and publishing:
5663
- **[`@cowprotocol/sdk-weiroll`](https://github.com/cowprotocol/cow-sdk/tree/main/packages/weiroll/README.md)** - Weiroll integration utilities
5764

5865
#### 🛠️ Development Packages
66+
5967
- **[`@cow-sdk/typescript-config`](https://github.com/cowprotocol/cow-sdk/tree/main/packages/typescript-config)** - Shared TypeScript configuration
6068

6169
### Development Workflow
@@ -83,3 +91,24 @@ pnpm typecheck
8391
pnpm clean
8492
```
8593

94+
## Contributing
95+
96+
We welcome contributions to the CoW Protocol SDK! Here's how to get started:
97+
98+
### 🚀 **Quick Start**
99+
100+
1. **Fork the repository** on GitHub
101+
2. **Clone your fork** locally:
102+
```bash
103+
git clone https://github.com/YOUR_USERNAME/cow-sdk.git
104+
cd cow-sdk
105+
```
106+
3. **Follow the [Development Workflow](#development-workflow)** above to set up your environment
107+
4. **Make your changes** following our [Contributor Guidelines](CONTRIBUTING.md)
108+
5. **Create a pull request** short after creating it, you will be asked to sign the CLA (Contributor License Agreement). A comment with instructions will be added to the PR.
109+
110+
### 📚 **Need Help?**
111+
112+
- Check existing [issues](https://github.com/cowprotocol/cow-sdk/issues) for similar problems
113+
- Join our [Discord](https://discord.gg/cowprotocol) for community support
114+
- Read the [CoW Protocol documentation](https://docs.cow.fi)

0 commit comments

Comments
 (0)