npm-workspaces-publish-tool 0.0.15 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -1
- package/build/package.json +1 -1
- package/build/src/cli.js +53 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,13 +23,16 @@ npm install -g npm-workspaces-publish-tool
|
|
|
23
23
|
## Usage
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
nw-publish [--dry-run] [--registry <url>]
|
|
26
|
+
nw-publish [--dry-run] [--registry <url>] [--auto-tick] [--anchor-tag-suffix <suffix>] [--allow-no-upstream]
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
### Command Options
|
|
30
30
|
|
|
31
31
|
- `--dry-run`: Runs validation and preview without actual publishing
|
|
32
32
|
- `--registry <url>`: (Optional) NPM registry URL to publish scoped packages to
|
|
33
|
+
- `--auto-tick`: Automatically increment patch versions for packages that need version bumps
|
|
34
|
+
- `--anchor-tag-suffix <suffix>`: (Optional) Suffix to append to git tags (e.g., `-rc` creates tags like `v1.2.3-rc`)
|
|
35
|
+
- `--allow-no-upstream`: Allow publishing from branches without upstream tracking (see [CI/CD Usage](#cicd-usage))
|
|
33
36
|
|
|
34
37
|
## Workflow
|
|
35
38
|
1. **Validation Phase**
|
|
@@ -43,6 +46,39 @@ nw-publish [--dry-run] [--registry <url>]
|
|
|
43
46
|
- Publishes packages in topological order
|
|
44
47
|
- Restores original `package.json` files after publishing
|
|
45
48
|
|
|
49
|
+
## CI/CD Usage
|
|
50
|
+
|
|
51
|
+
### Publishing from New Branches
|
|
52
|
+
|
|
53
|
+
By default, the tool requires branches to have upstream tracking configured (via `git push -u origin <branch>`). This is a safety check for manual usage to prevent accidentally publishing from local-only branches.
|
|
54
|
+
|
|
55
|
+
However, in CI/CD pipelines that create new release branches dynamically, you can use the `--allow-no-upstream` flag:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# CI/CD pipeline example
|
|
59
|
+
nw-publish --allow-no-upstream --anchor-tag-suffix=-rc
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**When the flag is set:**
|
|
63
|
+
- The tool will automatically set upstream tracking when pushing tags
|
|
64
|
+
- No manual `git push -u` is required beforehand
|
|
65
|
+
- The branch and tags will be pushed together
|
|
66
|
+
|
|
67
|
+
**When to use:**
|
|
68
|
+
- CI/CD pipelines that create new release branches (e.g., `RELEASE/v1.2.3`)
|
|
69
|
+
- Automated release workflows where the branch doesn't exist on remote yet
|
|
70
|
+
|
|
71
|
+
**When not to use:**
|
|
72
|
+
- Manual publishing from your local machine (push your branch first instead)
|
|
73
|
+
|
|
74
|
+
**Safety notes:**
|
|
75
|
+
- The flag only bypasses the "no upstream" check
|
|
76
|
+
- The tool still validates:
|
|
77
|
+
- Clean git status (no uncommitted changes)
|
|
78
|
+
- Unpushed commits (if upstream exists)
|
|
79
|
+
- Version increments
|
|
80
|
+
- Build success
|
|
81
|
+
|
|
46
82
|
## Requirements
|
|
47
83
|
- Node.js 16+
|
|
48
84
|
- Git
|
package/build/package.json
CHANGED
package/build/src/cli.js
CHANGED
|
@@ -123,7 +123,7 @@ function getDirtyMap(workspaces, lastTag, cwd) {
|
|
|
123
123
|
}
|
|
124
124
|
return dirtyMap;
|
|
125
125
|
}
|
|
126
|
-
function checkForUnpushedCommits(cwd, autoTick) {
|
|
126
|
+
function checkForUnpushedCommits(cwd, autoTick, allowNoUpstream) {
|
|
127
127
|
const branchResult = git(['rev-parse', '--abbrev-ref', 'HEAD'], { cwd });
|
|
128
128
|
if (!branchResult.success) {
|
|
129
129
|
console.error('ERROR: Could not determine current branch.');
|
|
@@ -141,8 +141,14 @@ function checkForUnpushedCommits(cwd, autoTick) {
|
|
|
141
141
|
console.log(`ℹ️ Branch '${currentBranch}' has no upstream. Will set it during push.`);
|
|
142
142
|
return true;
|
|
143
143
|
}
|
|
144
|
-
//
|
|
144
|
+
// If --allow-no-upstream is set, allow new branches (for CI/CD pipelines)
|
|
145
|
+
if (allowNoUpstream) {
|
|
146
|
+
console.warn(`⚠️ Branch '${currentBranch}' has no upstream. Upstream will be set automatically when pushing tags.`);
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
// In manual mode, require upstream to catch mistakes
|
|
145
150
|
console.error(`ERROR: Branch '${currentBranch}' has no upstream.`);
|
|
151
|
+
console.error(`Please push your branch first, or use --allow-no-upstream if this is intentional.`);
|
|
146
152
|
return false;
|
|
147
153
|
}
|
|
148
154
|
const upstream = upstreamResult.stdout.trim();
|
|
@@ -158,7 +164,7 @@ function checkForUnpushedCommits(cwd, autoTick) {
|
|
|
158
164
|
/**
|
|
159
165
|
* Check git status for uncommitted changes
|
|
160
166
|
*/
|
|
161
|
-
function checkGitStatus(workspaces, dirtyMap, cwd, dryRun, autoTick) {
|
|
167
|
+
function checkGitStatus(workspaces, dirtyMap, cwd, dryRun, autoTick, allowNoUpstream) {
|
|
162
168
|
const stagedFilesRaw = git(['diff', '--cached', '--name-only'], { cwd })
|
|
163
169
|
.stdout.trim()
|
|
164
170
|
.split('\n')
|
|
@@ -329,7 +335,7 @@ function checkGitStatus(workspaces, dirtyMap, cwd, dryRun, autoTick) {
|
|
|
329
335
|
}
|
|
330
336
|
}
|
|
331
337
|
}
|
|
332
|
-
return checkForUnpushedCommits(cwd, autoTick);
|
|
338
|
+
return checkForUnpushedCommits(cwd, autoTick, allowNoUpstream);
|
|
333
339
|
}
|
|
334
340
|
/**
|
|
335
341
|
* Get version info for dirty workspaces
|
|
@@ -494,7 +500,7 @@ function buildPackages(releaseOrder, packageInfos) {
|
|
|
494
500
|
}
|
|
495
501
|
}
|
|
496
502
|
}
|
|
497
|
-
function validatePublish(dryRun, autoTick) {
|
|
503
|
+
function validatePublish(dryRun, autoTick, allowNoUpstream) {
|
|
498
504
|
// Push any previous auto-tick commits if in full mode
|
|
499
505
|
if (!dryRun) {
|
|
500
506
|
const upstreamResult = git(['rev-parse', '--abbrev-ref', '@{u}'], {
|
|
@@ -538,7 +544,7 @@ function validatePublish(dryRun, autoTick) {
|
|
|
538
544
|
console.log('\n🗃️ Validating git status...\n');
|
|
539
545
|
const dirtyMap = getDirtyMap(workspaces, lastTag, cwd);
|
|
540
546
|
// Check git status
|
|
541
|
-
const gitClean = checkGitStatus(workspaces, dirtyMap, cwd, dryRun, autoTick);
|
|
547
|
+
const gitClean = checkGitStatus(workspaces, dirtyMap, cwd, dryRun, autoTick, allowNoUpstream);
|
|
542
548
|
if (!gitClean) {
|
|
543
549
|
console.error('❌ Git issues detected. Commit/stash changes before publishing.\n');
|
|
544
550
|
process.exit(1);
|
|
@@ -621,7 +627,7 @@ function validatePublish(dryRun, autoTick) {
|
|
|
621
627
|
console.log('\n🗃️ Re-validating...\n');
|
|
622
628
|
// In dry-run mode, we allow the auto-tick commit to remain unpushed
|
|
623
629
|
if (!dryRun) {
|
|
624
|
-
if (!checkForUnpushedCommits(cwd, autoTick)) {
|
|
630
|
+
if (!checkForUnpushedCommits(cwd, autoTick, allowNoUpstream)) {
|
|
625
631
|
process.exit(1);
|
|
626
632
|
}
|
|
627
633
|
}
|
|
@@ -734,7 +740,44 @@ function runNpmPublishInReleaseOrder(releaseOrder, packageInfos, packagesToRelea
|
|
|
734
740
|
}
|
|
735
741
|
}
|
|
736
742
|
}
|
|
743
|
+
/**
|
|
744
|
+
* Ensures the current branch has an upstream remote tracking branch.
|
|
745
|
+
* If no upstream exists, pushes the branch with -u to set it.
|
|
746
|
+
* This is necessary before pushing tags to ensure the branch is on remote.
|
|
747
|
+
*/
|
|
748
|
+
function ensureBranchHasUpstream() {
|
|
749
|
+
try {
|
|
750
|
+
const currentBranch = execSync('git branch --show-current', {
|
|
751
|
+
encoding: 'utf8',
|
|
752
|
+
}).trim();
|
|
753
|
+
// Check if upstream exists (command throws if not)
|
|
754
|
+
execSync(`git rev-parse --abbrev-ref ${currentBranch}@{u}`, {
|
|
755
|
+
encoding: 'utf8',
|
|
756
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
757
|
+
});
|
|
758
|
+
// Upstream exists - nothing to do
|
|
759
|
+
}
|
|
760
|
+
catch (err) {
|
|
761
|
+
// No upstream - set it by pushing current branch
|
|
762
|
+
console.log(`ℹ️ Branch has no upstream, setting it now...`);
|
|
763
|
+
const currentBranch = execSync('git branch --show-current', {
|
|
764
|
+
encoding: 'utf8',
|
|
765
|
+
}).trim();
|
|
766
|
+
try {
|
|
767
|
+
execSync(`git push -u origin ${currentBranch}`, {
|
|
768
|
+
stdio: 'inherit',
|
|
769
|
+
});
|
|
770
|
+
console.log(`✅ Upstream set for ${currentBranch}`);
|
|
771
|
+
}
|
|
772
|
+
catch (pushErr) {
|
|
773
|
+
console.error(`❌ Failed to set upstream for ${currentBranch}`);
|
|
774
|
+
throw pushErr;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
737
778
|
function tagAndPushRepo(version, tagSuffix) {
|
|
779
|
+
// Ensure branch has upstream before pushing tags
|
|
780
|
+
ensureBranchHasUpstream();
|
|
738
781
|
try {
|
|
739
782
|
execSync(`git tag v${version}${tagSuffix}`, { stdio: 'inherit' });
|
|
740
783
|
execSync(`git push origin v${version}${tagSuffix}`, {
|
|
@@ -765,13 +808,15 @@ program
|
|
|
765
808
|
.option('--registry <url>', 'NPM registry to publish to')
|
|
766
809
|
.option('--auto-tick', 'Automatically tick patch versions if needed')
|
|
767
810
|
.option('--anchor-tag-suffix <suffix>', "A tagging suffix convention which deviates from vx.x.x. E.g. if --anchor-tag-suffix=-rc is provided, tags (when not in dry-run mode) will be created in the form 'vx.x.x-rc'")
|
|
811
|
+
.option('--allow-no-upstream', 'Allow publishing from branches without upstream tracking. The tool will automatically set upstream when pushing tags. Intended for CI/CD pipelines that create new release branches.')
|
|
768
812
|
.action((options) => {
|
|
769
813
|
printHeader();
|
|
770
814
|
const dryRun = options.dryRun;
|
|
771
815
|
const registryUrl = options.registry;
|
|
772
816
|
const autoTick = options.autoTick;
|
|
773
817
|
const anchorTagSuffix = options.anchorTagSuffix ?? '';
|
|
774
|
-
const
|
|
818
|
+
const allowNoUpstream = options.allowNoUpstream ?? false;
|
|
819
|
+
const { packageInfos, releaseOrder, packagesToRelease, currentRootVersion, } = validatePublish(dryRun, autoTick, allowNoUpstream);
|
|
775
820
|
if (packagesToRelease.length === 0) {
|
|
776
821
|
return console.log('\n ̄\\_(ツ)_/ ̄ No workspaces to publish\n');
|
|
777
822
|
}
|