Skip to content

Commit 62deda7

Browse files
committed
[actions]: Add npm publish workflow
Signed-off-by: Varun Sharma <varunsh@stepsecurity.io>
1 parent bf59919 commit 62deda7

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

.github/workflows/npm-publish.yml

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
name: Publish Package to npm
2+
on:
3+
workflow_dispatch:
4+
inputs:
5+
tag:
6+
description: "Tag to publish"
7+
required: true
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
check-version:
14+
runs-on: ubuntu-latest
15+
outputs:
16+
is-new-version: ${{ steps.cpv.outputs.is-new-version }}
17+
steps:
18+
- uses: actions/checkout@v3
19+
with:
20+
ref: ${{ github.event.inputs.tag }}
21+
22+
- name: Match semver pattern
23+
uses: actions-ecosystem/action-regex-match@v2
24+
id: regex-match
25+
with:
26+
text: ${{ github.event.inputs.tag }}
27+
regex: '^v((([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)$'
28+
29+
- name: Check package version
30+
id: cpv
31+
uses: PostHog/check-package-version@v2
32+
33+
- name: Validate tag
34+
uses: actions/github-script@v6
35+
with:
36+
script: |
37+
const match = `${{ steps.regex-match.outputs.match }}`;
38+
if(match === '') {
39+
core.setFailed(`Tag ${context.payload.inputs.tag} does not match semver pattern`);
40+
} else {
41+
const isNewVersion = `${{ steps.cpv.outputs.is-new-version }}`;
42+
if(isNewVersion === 'true') {
43+
console.log(`Version ${context.payload.inputs.tag} has not been published yet`);
44+
} else {
45+
core.setFailed(`Version ${context.payload.inputs.tag} is already published`);
46+
}
47+
}
48+
check-status:
49+
needs: check-version
50+
if: needs.check-version.outputs.is-new-version == 'true'
51+
runs-on: ubuntu-latest
52+
steps:
53+
- name: Verify checks passed
54+
uses: actions/github-script@v6
55+
with:
56+
result-encoding: string
57+
retries: 3
58+
script: |
59+
console.log(`Checking status checks for ${context.payload.inputs.tag}`);
60+
61+
const check_suites = await github.rest.checks.listSuitesForRef({
62+
owner: context.repo.owner,
63+
repo: context.repo.repo,
64+
ref: context.payload.inputs.tag
65+
});
66+
67+
for (const check_suite of check_suites.data.check_suites) {
68+
if (check_suite.status !== 'completed') {
69+
core.setFailed(`Some workflows for ${context.payload.inputs.tag} are still in-progress`);
70+
}
71+
}
72+
73+
const branch = await github.rest.repos.getBranch({
74+
owner: context.repo.owner,
75+
repo: context.repo.repo,
76+
branch: context.payload.repository.default_branch
77+
});
78+
79+
for (const requiredCheck of branch.data.protection.required_status_checks.checks) {
80+
81+
const check_runs = await github.rest.checks.listForRef({
82+
owner: context.repo.owner,
83+
repo: context.repo.repo,
84+
ref: context.payload.inputs.tag,
85+
check_name: requiredCheck.context
86+
});
87+
88+
for (const check_run of check_runs.data.check_runs) {
89+
if (!(check_run.status === 'completed' && check_run.conclusion === 'success')){
90+
console.log(`${check_run.name} check failed`);
91+
core.setFailed(`Required status check ${check_run.name} did not succeed`);
92+
}
93+
console.log(`${check_run.name} check passed`);
94+
}
95+
}
96+
97+
publish:
98+
needs: check-status
99+
runs-on: ubuntu-latest
100+
permissions:
101+
contents: read
102+
id-token: write
103+
steps:
104+
- uses: step-security/harden-runner@v1
105+
with:
106+
egress-policy: block
107+
allowed-endpoints: >
108+
github.com:443
109+
hooks.slack.com:443
110+
prod.api.stepsecurity.io:443
111+
registry.npmjs.org:443
112+
113+
- uses: actions/checkout@v3
114+
with:
115+
ref: ${{ github.event.inputs.tag }}
116+
117+
- uses: actions/setup-node@v3
118+
with:
119+
node-version: "16.x"
120+
registry-url: "https://registry.npmjs.org"
121+
122+
- run: npm install
123+
124+
- uses: step-security/wait-for-secrets@v1
125+
id: wait-for-secrets
126+
with:
127+
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
128+
secrets: |
129+
OTP:
130+
name: 'OTP to publish package'
131+
description: 'OTP from authenticator app'
132+
133+
- run: npm publish --access public --otp ${{ steps.wait-for-secrets.outputs.OTP }}
134+
env:
135+
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}

0 commit comments

Comments
 (0)