diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 000000000..e5b6d8d6a --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,8 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works +with multi-package repos, or single-package repos to help you version and publish your code. You can +find the full documentation for it [in our repository](https://github.com/changesets/changesets) + +We have a quick list of common questions to get you started engaging with this project in +[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 000000000..dfe3bb47a --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [["@stacks/*"]], + "linked": [], + "access": "public", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/.github/DEVELOPMENT.md b/.github/DEVELOPMENT.md index 6caa8e4b5..af28e90b1 100644 --- a/.github/DEVELOPMENT.md +++ b/.github/DEVELOPMENT.md @@ -1,18 +1,16 @@ ## Adding dependencies -This repo uses Lerna [hoisting](https://github.com/lerna/lerna/blob/main/doc/hoist.md) for package dependencies. +This repo uses [npm workspaces](https://docs.npmjs.com/cli/v10/using-npm/workspaces) for package management. -In order to install a new dependency to a package, the [`lerna add`](https://github.com/lerna/lerna/tree/main/commands/add) command must be used, rather than `npm install `. - -For example, the following command installs `lodash` as a dependency to the `@stacks/storage` package: +To install a new dependency to a specific package, use the `-w` (workspace) flag: ```shell # Run within the root directory -npx lerna add lodash --scope @stacks/storage +npm install lodash -w @stacks/storage ``` -Add `--dev` to install as a development dependency: +Add `--save-dev` to install as a development dependency: ```shell -npx lerna add lodash --scope @stacks/storage --dev +npm install lodash -w @stacks/storage --save-dev ``` diff --git a/.github/scripts/snapshot-version.mjs b/.github/scripts/snapshot-version.mjs new file mode 100755 index 000000000..b258ced94 --- /dev/null +++ b/.github/scripts/snapshot-version.mjs @@ -0,0 +1,73 @@ +#!/usr/bin/env node +// Rewrite packages/*/package.json to a snapshot version like +// `-.` (or `-pr..`). Run between +// `changeset version` and `changeset publish --tag `. +// +// Usage: node .github/scripts/snapshot-version.mjs [--pr ] + +import { readFile, writeFile, readdir, appendFile } from 'node:fs/promises'; +import { execFileSync } from 'node:child_process'; + +const [tag, , prNumber] = process.argv.slice(2); +if (!['beta', 'pr'].includes(tag) || (tag === 'pr' && !prNumber)) { + console.error('Usage: snapshot-version.mjs [--pr ]'); + process.exit(1); +} + +const DEP_TYPES = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']; + +const entries = await readdir('packages', { withFileTypes: true }); +const pkgs = (await Promise.all( + entries + .filter((e) => e.isDirectory()) + .map((e) => `packages/${e.name}/package.json`) + .map(async (file) => { + try { return { file, json: JSON.parse(await readFile(file, 'utf8')) }; } + catch { return null; } + }), +)).filter(Boolean); + +const rep = pkgs.find(({ json }) => json.name && !json.private && /^\d+\.\d+\.\d+$/.test(json.version)); +if (!rep) { + console.error('No non-private workspace package with plain semver — did you run `changeset version` first?'); + process.exit(1); +} + +const prefix = tag === 'beta' ? `${rep.json.version}-beta.` : `${rep.json.version}-pr.${prNumber}.`; +const version = `${prefix}${nextCounter(rep.json.name, prefix)}`; +console.log(`Snapshot version: ${version} (base from ${rep.json.name})`); + +const internals = new Set(pkgs.map(({ json }) => json.name).filter(Boolean)); +const named = pkgs.filter(({ json }) => json.name); + +await Promise.all(named.map(({ file, json }) => + writeFile(file, JSON.stringify(rewrite(json, version, internals), null, 2) + '\n'), +)); +named.forEach(({ json }) => console.log(` ${json.name} → ${version}`)); + +if (process.env.GITHUB_OUTPUT) await appendFile(process.env.GITHUB_OUTPUT, `version=${version}\n`); + +function rewrite(json, version, internals) { + const rewriteDeps = (deps) => + Object.fromEntries(Object.entries(deps).map(([n, v]) => [n, internals.has(n) ? version : v])); + return DEP_TYPES.reduce( + (acc, dt) => (acc[dt] ? { ...acc, [dt]: rewriteDeps(acc[dt]) } : acc), + { ...json, version }, + ); +} + +function nextCounter(name, prefix) { + let raw; + try { + raw = execFileSync('npm', ['view', name, 'versions', '--json'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }); + } catch (e) { + if ((e.stderr || '').includes('E404')) return 0; + throw e; + } + const all = JSON.parse(raw); + const counters = (Array.isArray(all) ? all : [all]) + .filter((v) => v.startsWith(prefix)) + .map((v) => Number(v.slice(prefix.length))) + .filter(Number.isInteger); + return counters.length ? Math.max(...counters) + 1 : 0; +} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9e0280d08..d007aec03 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,63 +1,137 @@ name: publish +# All npm-publishing flows live in this single file. npm's trusted-publisher +# config for every `@stacks/*` package points at `publish.yml` — renaming this +# file requires updating that config on npmjs.com per package. +# +# release — push to main / workflow_dispatch. +# changesets/action opens/updates the "chore: version packages" +# PR. When that PR is merged, the same action publishes the +# bumped packages to npm under `latest`. `commitMode: github-api` +# gives commits a Verified badge without signing keys in CI. +# +# beta — push to main. +# Snapshot-publishes every push to `@beta`, with versions of the +# form `-beta.` (counter scoped to the base +# version, queried from npm). +# +# pr — pull_request. +# Same as `beta` but `@pr` dist-tag and versions of the form +# `-pr..`. Sticky-header'd into the PR +# description so reviewers can install the canary build. +# +# Forks never reach the publish step (`is-not-fork` gate from pre-run). + on: push: branches: - main tags-ignore: - - "**" + - '**' paths-ignore: - - "**.md" + - '**.md' workflow_dispatch: + inputs: + pr-number: + description: 'PR number to publish a canary build for (leave blank for a normal release dispatch)' + type: string + required: false + default: '' permissions: - id-token: write # Required for OIDC - contents: read + contents: write + pull-requests: write + id-token: write # npm OIDC trusted publishing + +concurrency: ${{ github.workflow }}-${{ github.ref }} jobs: pre-run: uses: ./.github/workflows/pre-run.yml secrets: inherit - publish: + release: + runs-on: ubuntu-latest + needs: pre-run + if: needs.pre-run.outputs.is-not-fork == 'true' && (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.pr-number == '')) + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: 24 + cache: npm + + - run: npm ci + + - name: Create release PR or publish + uses: changesets/action@63a615b9cd06ba9a3e6d13796c7fbcb080a60a0b # v1.8.0 + with: + version: npm run version + publish: npm run release + title: 'chore: version packages' + commit: 'chore: version packages' + commitMode: github-api + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + beta: runs-on: ubuntu-latest needs: pre-run - if: "needs.pre-run.outputs.is-not-fork == 'true' && (github.event_name == 'workflow_dispatch' || contains(github.event.head_commit.message, 'chore: version packages'))" + if: needs.pre-run.outputs.is-not-fork == 'true' && github.event_name == 'push' steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 24 cache: npm - run: npm ci - - run: npm run build - - run: git config --local user.email 'action@github.com' - - run: git config --local user.name 'GitHub Action' + - name: Seed dummy changeset if none present + run: | + if ! ls .changeset/*.md 2>/dev/null | grep -vqi readme; then + cat > .changeset/__snapshot.md <<'EOF' + --- + "@stacks/common": patch + --- + + snapshot + EOF + fi - - name: Publish + - run: npx changeset version + - run: node .github/scripts/snapshot-version.mjs beta + + - name: Build packages run: | - npx lerna publish from-package --no-verify-access --yes - RELEASE=$(cat lerna.json | jq -r '.version') - git tag -a v$RELEASE -m v$RELEASE - git push origin v$RELEASE + npm run clean --workspaces --if-present + npm run build:cjs --workspaces --if-present + npm run build:esm --workspaces --if-present + npm run build:umd --workspaces --if-present + + # NPM_CONFIG_IGNORE_SCRIPTS skips each package's prepublishOnly hook, + # avoiding duplicate test/build work during publish. + - run: npx changeset publish --tag beta --no-git-tag + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_CONFIG_IGNORE_SCRIPTS: 'true' - publish-beta: + pr: runs-on: ubuntu-latest needs: pre-run - if: "needs.pre-run.outputs.is-not-fork == 'true' && !contains(github.event.head_commit.message, 'chore: version packages')" + if: needs.pre-run.outputs.is-not-fork == 'true' && github.event_name == 'workflow_dispatch' && inputs.pr-number != '' steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - # pulls all tags (needed for lerna to correctly version) - - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 24 @@ -65,18 +139,39 @@ jobs: - run: npm ci - - run: git config --local user.email 'action@github.com' - - run: git config --local user.name 'GitHub Action' + - name: Seed dummy changeset if none present + run: | + if ! ls .changeset/*.md 2>/dev/null | grep -vqi readme; then + cat > .changeset/__snapshot.md <<'EOF' + --- + "@stacks/common": patch + --- + + snapshot + EOF + fi + + - run: npx changeset version + - id: snapshot + run: node .github/scripts/snapshot-version.mjs pr --pr ${{ inputs.pr-number }} - - name: Version & Publish + - name: Build packages run: | - LATEST=$(git describe --tags --abbrev=0) - npx lerna version $LATEST --force-publish --no-changelog --no-push --no-git-tag-version --yes - git commit -a -m 'DONT PUSH: fast-forward to latest npm release' --allow-empty --no-verify - npx lerna version --force-publish --no-changelog --conventional-commits --conventional-prerelease --preid beta --no-push --no-git-tag-version --yes - git commit -a -m 'DONT PUSH: version beta release' --allow-empty --no-verify - RELEASE=$(cat lerna.json | jq -r '.version') - npx lerna publish from-package --no-verify-access --pre-dist-tag beta --yes - git reset HEAD~2 --hard - git tag v$RELEASE - git push origin v$RELEASE + npm run clean --workspaces --if-present + npm run build:cjs --workspaces --if-present + npm run build:esm --workspaces --if-present + npm run build:umd --workspaces --if-present + + - run: npx changeset publish --tag pr --no-git-tag + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_CONFIG_IGNORE_SCRIPTS: 'true' + + - name: Update PR sticky header + uses: janniks/pull-request-fixed-header@57bc082ecbd040df344747d5b8644af5fad02c95 # v1.2.0 + with: + header: | + > This PR was published to npm with the version `${{ steps.snapshot.outputs.version }}` (dist-tag `pr`). + > e.g. `npm install @stacks/common@${{ steps.snapshot.outputs.version }} --save-exact` + pr-number: ${{ inputs.pr-number }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 9dcfdbd7d..f6daa2789 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -26,54 +26,3 @@ jobs: needs: pre-run uses: ./.github/workflows/tests.yml secrets: inherit - - # FOR NOW DISABLE PUBLISHING TO NPM ON EACH PULL-REQUEST PUSH - # publish-pr: - # runs-on: ubuntu-latest - # needs: [pre-run, tests] - # if: needs.pre-run.outputs.is-not-fork == 'true' - # steps: - # - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - # if: github.event_name == 'pull_request' - # with: - # ref: ${{ github.event.pull_request.head.ref }} - # fetch-depth: 0 - # - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - # if: github.event_name == 'workflow_dispatch' - # with: - # fetch-depth: 0 - # - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - # with: - # node-version: 24 - # cache: npm - # - run: npm install -g npm@latest && npm ci - # - name: Set up npm auth token - # run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc - # env: - # NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - # - name: Publish to npm (feature branch) - # id: publish-pr - # run: | - # PUBLISH_OUTPUT_FILE=$(mktemp) - # npx lerna publish --canary --force-publish --preid pr --dist-tag pr --no-verify-access --no-push --no-git-tag-version --yes | tee $PUBLISH_OUTPUT_FILE - # VERSION=$(grep -oP '@stacks/common\s*=>\s*\K[^\s]+' $PUBLISH_OUTPUT_FILE | head -n 1) - # echo "Canary version: $VERSION" - # echo "::set-output name=version::$VERSION" - # if: ${{ (github.head_ref || github.ref_name) != 'nakamoto' && (github.head_ref || github.ref_name) != 'next' }} - # env: - # SKIP_TESTS: true - # - name: Publish to npm (next/nakamoto) - # run: | - # git reset --hard - # npx lerna publish --canary major --force-publish --preid $BRANCH --dist-tag $BRANCH --no-verify-access --no-push --no-git-tag-version --yes - # if: ${{ (github.head_ref || github.ref_name) == 'nakamoto' || (github.head_ref || github.ref_name) == 'next' }} - # env: - # BRANCH: ${{ github.head_ref || github.ref_name }} - # SKIP_TESTS: true - # - name: Edit pull-request description with published version - # uses: janniks/pull-request-fixed-header@v1.0.1 - # with: - # header: "> This PR was published to npm with the version `${{ steps.publish-pr.outputs.version || env.BRANCH }}`\n> e.g. `npm install @stacks/common@${{ steps.publish-pr.outputs.version || env.BRANCH }} --save-exact`" - # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # env: - # BRANCH: ${{ github.head_ref || github.ref_name }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6d798b721..7a9cb5ca3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -40,7 +40,7 @@ jobs: run: npm run madge - name: Run tests - run: npm run lerna run test --stream --parallel -- -- --coverage + run: npm run test --workspaces --if-present -- --coverage - name: Upload test coverage uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml deleted file mode 100644 index 4b94b9eea..000000000 --- a/.github/workflows/version.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: version - -on: - push: - branches: - - main - tags-ignore: - - '**' - paths-ignore: - - '**.md' - - workflow_dispatch: - -env: - COMMIT_USER: Hiro DevOps - COMMIT_EMAIL: 45208873+blockstack-devops@users.noreply.github.com - -jobs: - pre-run: - uses: ./.github/workflows/pre-run.yml - secrets: inherit - - version: - runs-on: ubuntu-latest - needs: pre-run - if: "needs.pre-run.outputs.is-not-fork == 'true' && !contains(github.event.head_commit.message, 'chore: version packages')" - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - ref: main - - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version: 24 - cache: npm - - - run: npm install -g npm@latest - - run: npm ci - - # workaround for changesets/action to allow not using changesets - # todo: add `force` option on fork to allow removing this workaround - - run: | - npx changeset init - npx changeset --empty - echo '.changeset/' >> .git/info/exclude - - - run: git tag -d $(git tag | grep 'beta') - - - name: Create Release Pull Request - uses: janniks/changesets-action@main - with: - title: 'chore: version packages' - commit: 'chore: version packages' - version: 'npm run ci:version' - env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 33e360d7c..c0a9b33ce 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Stacks.js [![Test Action Badge](https://github.com/hirosystems/stacks.js/actions/workflows/tests.yml/badge.svg)](https://github.com/hirosystems/stacks.js/actions/workflows/tests.yml) [![Monorepo Version Label](https://img.shields.io/github/lerna-json/v/hirosystems/stacks.js?label=monorepo)](https://github.com/hirosystems/stacks.js/tree/main/packages) +# Stacks.js [![Test Action Badge](https://github.com/stx-labs/stacks.js/actions/workflows/tests.yml/badge.svg)](https://github.com/stx-labs/stacks.js/actions/workflows/tests.yml) [![Monorepo Version Label](https://img.shields.io/github/package-json/v/stx-labs/stacks.js?filename=packages%2Fcommon%2Fpackage.json&label=monorepo)](https://github.com/stx-labs/stacks.js/tree/main/packages) > [!IMPORTANT] > Looking for Nakamoto updates? diff --git a/configs/webpack.config.js b/configs/webpack.config.js index ae267b063..52591afb8 100644 --- a/configs/webpack.config.js +++ b/configs/webpack.config.js @@ -2,7 +2,7 @@ const webpack = require('webpack'); const path = require('path'); // Run with ANALYZE ENV to show bundle size (only works with cjs) -// e.g.: ANALYZE=true lerna run --scope @stacks/wallet-sdk build +// e.g.: ANALYZE=true npm run build -w @stacks/wallet-sdk const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; const { DuplicatesPlugin } = require('inspectpack/plugin'); diff --git a/lerna.json b/lerna.json deleted file mode 100644 index 667fc6130..000000000 --- a/lerna.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "version": "7.4.0", - "registry": "https://registry.npmjs.org/", - "publishConfig": { - "access": "public" - }, - "changelogPreset": "conventionalcommits", - "packages": [ - "packages/*" - ], - "$schema": "node_modules/lerna/schemas/lerna-schema.json" -} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 753580976..496f2c390 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,6 @@ "@types/bs58check": "^2.1.0", "@types/jest": "^29.5.3", "assert": "^2.0.0", - "conventional-changelog-conventionalcommits": "^6.1.0", "esbuild": "^0.19.0", "esbuild-loader": "^4.0.0", "husky": "^8.0.3", diff --git a/package.json b/package.json index 08bb580b7..933a3824a 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,9 @@ ], "license": "MIT", "scripts": { - "build": "lerna run build", + "build": "npm run build --workspaces --if-present", "build:docs": "rimraf docs && typedoc", - "ci:version": "npx lerna version --conventional-commits --no-push --no-git-tag-version --yes", - "clean": "lerna clean", - "lerna": "lerna", + "clean": "npm run clean --workspaces --if-present", "lint": "npm run lint:check && npm run fmt:check", "lint:check": "oxlint packages/**/src/", "lint:fix": "oxlint --fix packages/**/src/", @@ -18,9 +16,11 @@ "fmt:check": "oxfmt --check packages/**/src/", "fix": "npm run lint:fix && npm run fmt", "madge": "madge --circular --extensions ts --exclude 'd.ts' packages/", - "pack": "lerna run pack", - "test": "lerna run test", - "typecheck": "lerna run typecheck --parallel --no-bail --stream", + "pack": "npm run pack --workspaces --if-present", + "release": "npm run build && changeset publish", + "test": "npm run test --workspaces --if-present", + "typecheck": "npm run typecheck --workspaces --if-present", + "version": "changeset version", "prepare": "husky install .github/.husky" }, "devDependencies": { @@ -30,7 +30,6 @@ "@types/bs58check": "^2.1.0", "@types/jest": "^29.5.3", "assert": "^2.0.0", - "conventional-changelog-conventionalcommits": "^6.1.0", "esbuild": "^0.19.0", "esbuild-loader": "^4.0.0", "husky": "^8.0.3", @@ -38,7 +37,6 @@ "jest": "^29.6.2", "jest-fetch-mock": "^3.0.3", "jest-module-name-mapper": "^0.1.5", - "lerna": "^9.0.0", "madge": "^6.1.0", "oxfmt": "^0.42.0", "oxlint": "^1.57.0", diff --git a/packages/bns/package.json b/packages/bns/package.json index 5577e806c..fd8381bd5 100755 --- a/packages/bns/package.json +++ b/packages/bns/package.json @@ -48,7 +48,7 @@ ], "repository": { "type": "git", - "url": "git+https://github.com/blockstack/stacks.js.git" + "url": "git+https://github.com/stx-labs/stacks.js.git" }, "bugs": { "url": "https://github.com/blockstack/stacks.js/issues"