@@ -8,7 +8,7 @@ import { $ } from "execa";
88 * subcommand and passed through every subsequent step via GitHub Actions job
99 * outputs + per-step flags.
1010 */
11- export type Trigger = "pr" | "main " | "release" ;
11+ export type Trigger = "branch " | "release" ;
1212
1313export interface PublishContext {
1414 trigger : Trigger ;
@@ -20,7 +20,8 @@ export interface PublishContext {
2020 sha : string ;
2121 /** Only meaningful when trigger === "release". */
2222 latest : boolean ;
23- prNumber ?: number ;
23+ /** Branch name. Only set when trigger === "branch". */
24+ branch ?: string ;
2425 repoRoot : string ;
2526}
2627
@@ -29,7 +30,7 @@ export interface ResolveOverrides {
2930 trigger ?: Trigger ;
3031 version ?: string ;
3132 latest ?: boolean ;
32- prNumber ?: number ;
33+ branch ?: string ;
3334 sha ?: string ;
3435}
3536
@@ -47,10 +48,11 @@ function findRepoRoot(): string {
4748
4849/**
4950 * Base for all preview pre-release strings. Hardcoded to `0.0.0` so preview
50- * versions like `0.0.0-pr.4600 .abc1234` never look like real releases and
51+ * versions like `0.0.0-my-branch .abc1234` never look like real releases and
5152 * always sort below any published `X.Y.Z`. Using a committed `package.json`
5253 * version as the base would just embed whatever stale number happened to be
53- * committed there — it has no semantic relationship to the PR being previewed.
54+ * committed there. It has no semantic relationship to the branch being
55+ * previewed.
5456 */
5557const PREVIEW_BASE_VERSION = "0.0.0" ;
5658
@@ -61,22 +63,31 @@ async function readShortSha(repoRoot: string): Promise<string> {
6163 return stdout . trim ( ) . slice ( 0 , 7 ) ;
6264}
6365
64- function readPrNumberFromEvent ( ) : number | undefined {
65- const path = process . env . GITHUB_EVENT_PATH ;
66- if ( ! path || ! existsSync ( path ) ) return undefined ;
67- try {
68- const event = JSON . parse ( readFileSync ( path , "utf-8" ) ) as {
69- pull_request ?: { number ?: number } ;
70- number ?: number ;
71- } ;
72- if ( typeof event . pull_request ?. number === "number" ) {
73- return event . pull_request . number ;
74- }
75- if ( typeof event . number === "number" ) return event . number ;
76- } catch {
77- // fall through
66+ async function readBranchName ( repoRoot : string ) : Promise < string > {
67+ const envRef = process . env . GITHUB_REF_NAME ;
68+ if ( envRef ) return envRef ;
69+ const { stdout } = await $ ( {
70+ cwd : repoRoot ,
71+ } ) `git rev-parse --abbrev-ref HEAD` ;
72+ return stdout . trim ( ) ;
73+ }
74+
75+ /**
76+ * Sanitize a branch name into something safe to use as both an npm dist-tag
77+ * and a semver prerelease identifier. Lowercases, replaces any
78+ * non-alphanumeric character (other than hyphens) with a hyphen, collapses
79+ * runs of hyphens, and trims leading/trailing hyphens.
80+ */
81+ function sanitizeBranch ( branch : string ) : string {
82+ const cleaned = branch
83+ . toLowerCase ( )
84+ . replace ( / [ ^ a - z 0 - 9 - ] + / g, "-" )
85+ . replace ( / - + / g, "-" )
86+ . replace ( / ^ - + | - + $ / g, "" ) ;
87+ if ( cleaned . length === 0 ) {
88+ throw new Error ( `branch name "${ branch } " sanitized to empty string` ) ;
7889 }
79- return undefined ;
90+ return cleaned ;
8091}
8192
8293function readInputFromEvent < T = unknown > ( name : string ) : T | undefined {
@@ -105,10 +116,11 @@ function parseBoolInput(v: unknown, fallback: boolean): boolean {
105116function deriveTrigger ( overrides : ResolveOverrides | undefined ) : Trigger {
106117 if ( overrides ?. trigger ) return overrides . trigger ;
107118 const eventName = process . env . GITHUB_EVENT_NAME ;
108- if ( eventName === "pull_request" || eventName === "pull_request_target" )
109- return "pr" ;
110- if ( eventName === "workflow_dispatch" ) return "release" ;
111- if ( eventName === "push" ) return "main" ;
119+ if ( eventName === "workflow_dispatch" ) {
120+ const version = readInputFromEvent < string > ( "version" ) ;
121+ if ( typeof version === "string" && version . length > 0 ) return "release" ;
122+ return "branch" ;
123+ }
112124 // Default for local invocation without overrides (unusual): assume release
113125 // so missing fields are caught loudly.
114126 return "release" ;
@@ -118,15 +130,14 @@ function computeNpmTag(
118130 trigger : Trigger ,
119131 version : string ,
120132 latest : boolean ,
121- prNumber ?: number ,
133+ branch ?: string ,
122134) : string {
123- if ( trigger === "pr " ) {
124- if ( typeof prNumber !== "number" ) {
125- throw new Error ( "PR trigger requires prNumber to compute npm tag" ) ;
135+ if ( trigger === "branch " ) {
136+ if ( ! branch ) {
137+ throw new Error ( "branch trigger requires branch to compute npm tag" ) ;
126138 }
127- return `pr- ${ prNumber } ` ;
139+ return sanitizeBranch ( branch ) ;
128140 }
129- if ( trigger === "main" ) return "main" ;
130141 // release
131142 if ( version . includes ( "-rc." ) ) return "rc" ;
132143 return latest ? "latest" : "next" ;
@@ -136,17 +147,16 @@ function computeVersion(
136147 trigger : Trigger ,
137148 base : string ,
138149 sha : string ,
139- prNumber : number | undefined ,
150+ branch : string | undefined ,
140151 overrideVersion : string | undefined ,
141152) : string {
142153 if ( overrideVersion ) return overrideVersion ;
143- if ( trigger === "pr " ) {
144- if ( typeof prNumber !== "number" ) {
145- throw new Error ( "PR trigger requires prNumber to compute version" ) ;
154+ if ( trigger === "branch " ) {
155+ if ( ! branch ) {
156+ throw new Error ( "branch trigger requires branch to compute version" ) ;
146157 }
147- return `${ base } -pr. ${ prNumber } .${ sha } ` ;
158+ return `${ base } -${ sanitizeBranch ( branch ) } .${ sha } ` ;
148159 }
149- if ( trigger === "main" ) return `${ base } -main.${ sha } ` ;
150160 throw new Error ( "release trigger requires an explicit version override" ) ;
151161}
152162
@@ -164,9 +174,9 @@ export async function resolveContext(
164174
165175 const sha = overrides . sha ?? ( await readShortSha ( repoRoot ) ) ;
166176
167- let prNumber = overrides . prNumber ;
168- if ( trigger === "pr " && prNumber === undefined ) {
169- prNumber = readPrNumberFromEvent ( ) ;
177+ let branch = overrides . branch ;
178+ if ( trigger === "branch " && ! branch ) {
179+ branch = await readBranchName ( repoRoot ) ;
170180 }
171181
172182 // Release version: override > workflow_dispatch input > error.
@@ -181,7 +191,7 @@ export async function resolveContext(
181191 trigger ,
182192 PREVIEW_BASE_VERSION ,
183193 sha ,
184- prNumber ,
194+ branch ,
185195 version ,
186196 ) ;
187197 } else if ( ! version ) {
@@ -198,15 +208,15 @@ export async function resolveContext(
198208 }
199209 if ( trigger !== "release" ) latest = false ;
200210
201- const npmTag = computeNpmTag ( trigger , version , latest , prNumber ) ;
211+ const npmTag = computeNpmTag ( trigger , version , latest , branch ) ;
202212
203213 return {
204214 trigger,
205215 version,
206216 npmTag,
207217 sha,
208218 latest,
209- prNumber ,
219+ branch ,
210220 repoRoot,
211221 } ;
212222}
@@ -221,7 +231,7 @@ export function writeContextToGithubOutput(ctx: PublishContext): void {
221231 console . log ( `npm_tag=${ ctx . npmTag } ` ) ;
222232 console . log ( `sha=${ ctx . sha } ` ) ;
223233 console . log ( `latest=${ ctx . latest } ` ) ;
224- if ( ctx . prNumber !== undefined ) console . log ( `pr_number =${ ctx . prNumber } ` ) ;
234+ if ( ctx . branch !== undefined ) console . log ( `branch =${ ctx . branch } ` ) ;
225235 return ;
226236 }
227237 const lines = [
@@ -231,7 +241,7 @@ export function writeContextToGithubOutput(ctx: PublishContext): void {
231241 `sha=${ ctx . sha } ` ,
232242 `latest=${ ctx . latest } ` ,
233243 ] ;
234- if ( ctx . prNumber !== undefined ) lines . push ( `pr_number =${ ctx . prNumber } ` ) ;
244+ if ( ctx . branch !== undefined ) lines . push ( `branch =${ ctx . branch } ` ) ;
235245 // Append (do not overwrite) in case other steps also wrote to GITHUB_OUTPUT.
236246 appendFileSync ( path , `${ lines . join ( "\n" ) } \n` ) ;
237247}
0 commit comments