bitmovin-player-ui 3.102.0 → 3.102.1-beta.1

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.
@@ -0,0 +1,15 @@
1
+ name: 'Setup Dependencies'
2
+ description: 'Sets up Node.js, caches node modules, and installs dependencies'
3
+
4
+ runs:
5
+ using: 'composite'
6
+ steps:
7
+ - name: Set up node.js
8
+ uses: actions/setup-node@v5
9
+ with:
10
+ node-version-file: .nvmrc
11
+ cache: 'npm'
12
+
13
+ - name: Install dependencies
14
+ run: npm ci
15
+ shell: bash
@@ -0,0 +1,61 @@
1
+ const semver = require('semver');
2
+
3
+ /**
4
+ * Calculates the full version number based on release type and input version
5
+ *
6
+ * @param {object} core - GitHub Actions core object for logging
7
+ * @param {string} inputVersionNumber - Base version number (e.g., "3.102.1")
8
+ * @param {string} releaseType - Type of release (alpha, beta, rc, final)
9
+ * @param {string} latestTag - Latest existing tag for this version and release type - optional
10
+ * @returns {object} - Returns object with version_number, major_version, and tag_name
11
+ */
12
+ function calculateVersionNumber(core, inputVersionNumber, releaseType, latestTag = '') {
13
+ const isValid = semver.valid(inputVersionNumber);
14
+ if (!isValid) {
15
+ throw new Error(`Invalid version number: ${inputVersionNumber}`);
16
+ }
17
+
18
+ let fullVersion;
19
+ if (releaseType === 'final') {
20
+ fullVersion = inputVersionNumber;
21
+ } else {
22
+ // Use long prerelease identifiers directly (alpha, beta, rc)
23
+ const preReleaseTag = releaseType;
24
+ if (!['alpha', 'beta', 'rc'].includes(preReleaseTag)) {
25
+ throw new Error(`Invalid release type: ${releaseType}`);
26
+ }
27
+
28
+ let nextNumber = 1;
29
+ if (latestTag) {
30
+ const match = latestTag.match(new RegExp(`^v${inputVersionNumber.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}-${preReleaseTag}\\.(\\d+)$`));
31
+ if (match) {
32
+ nextNumber = parseInt(match[1]) + 1;
33
+ }
34
+ }
35
+
36
+ fullVersion = `${inputVersionNumber}-${preReleaseTag}.${nextNumber}`;
37
+ core.info(`Pre-release tag: ${preReleaseTag}, Latest tag: ${latestTag || 'none'}, next number: ${nextNumber}`);
38
+ }
39
+
40
+ const isFullVersionValid = semver.valid(fullVersion);
41
+ if (!isFullVersionValid) {
42
+ throw new Error(`Generated invalid version: ${fullVersion}`);
43
+ }
44
+
45
+ const majorVersion = semver.major(fullVersion);
46
+ const tagName = `v${fullVersion}`;
47
+
48
+ core.info(`Input version: ${inputVersionNumber}`);
49
+ core.info(`Release type: ${releaseType}`);
50
+ core.info(`Full version: ${fullVersion}`);
51
+ core.info(`Major version: ${majorVersion}`);
52
+ core.info(`Tag name: ${tagName}`);
53
+
54
+ return {
55
+ version_number: fullVersion,
56
+ major_version: majorVersion,
57
+ tag_name: tagName
58
+ };
59
+ }
60
+
61
+ module.exports.calculateVersionNumber = calculateVersionNumber;
@@ -0,0 +1,46 @@
1
+ const fs = require('fs');
2
+
3
+ /**
4
+ * Detects the release level (minor or patch) based on changelog content
5
+ *
6
+ * @param {object} core - GitHub Actions core object for logging
7
+ * @param {string} changelogPath - Path to the changelog file (defaults to './CHANGELOG.md')
8
+ * @returns {string} - Returns 'minor' or 'patch'
9
+ */
10
+ function detectReleaseLevel(core, changelogPath = './CHANGELOG.md') {
11
+ const changelogContent = fs.readFileSync(changelogPath, { encoding: 'utf8', flag: 'r' });
12
+
13
+ const unreleasedMatch = changelogContent.match(/## \[?unreleased\]?/i);
14
+ if (!unreleasedMatch) {
15
+ throw new Error('No unreleased section found in CHANGELOG.md');
16
+ }
17
+
18
+ const unreleasedStart = unreleasedMatch.index + unreleasedMatch[0].length;
19
+ const nextSectionMatch = changelogContent.substring(unreleasedStart).match(/^## /m);
20
+ const unreleasedEnd = nextSectionMatch ? unreleasedStart + nextSectionMatch.index : changelogContent.length;
21
+ const unreleasedContent = changelogContent.substring(unreleasedStart, unreleasedEnd);
22
+
23
+ core.info('Unreleased section content:');
24
+ core.info(unreleasedContent);
25
+
26
+ const hasAdded = /### added/i.test(unreleasedContent);
27
+ const hasChanged = /### changed/i.test(unreleasedContent);
28
+ const hasRemoved = /### removed/i.test(unreleasedContent);
29
+ const hasFixed = /### fixed/i.test(unreleasedContent);
30
+
31
+ let releaseLevel;
32
+ if (hasAdded || hasChanged || hasRemoved) {
33
+ releaseLevel = 'minor';
34
+ core.info('Found Added, Changed, or Removed sections - will create minor release');
35
+ } else if (hasFixed) {
36
+ releaseLevel = 'patch';
37
+ core.info('Found only Fixed sections - will create patch release');
38
+ } else {
39
+ throw new Error('No valid changelog entries found in unreleased section');
40
+ }
41
+
42
+ core.info(`Release level: ${releaseLevel}`);
43
+ return releaseLevel;
44
+ }
45
+
46
+ module.exports.detectReleaseLevel = detectReleaseLevel;
@@ -1,10 +1,11 @@
1
1
  const fs = require('fs');
2
2
  const https = require('https');
3
3
 
4
- const jobStatus = process.argv[2];
5
- const changelogPath = process.argv[3];
6
- const slackWebhookUrl = process.argv[4];
7
- const runId = process.argv[5];
4
+ const versionNumber = process.argv[2];
5
+ const jobStatus = process.argv[3];
6
+ const changelogPath = process.argv[4];
7
+ const slackWebhookUrl = process.argv[5];
8
+ const runId = process.argv[6];
8
9
 
9
10
  const failureSlackChannelId = 'CGRK9DV7H';
10
11
  const successSlackChannelId = 'C0LJ16JBS';
@@ -15,16 +16,9 @@ fs.readFile(changelogPath, 'utf8', (err, fileContent) => {
15
16
  }
16
17
 
17
18
  const changelogContent = parseChangelogEntry(fileContent);
18
- const releaseVersion = parseReleaseVersion(fileContent);
19
- sendSlackMessage(releaseVersion, changelogContent);
19
+ sendSlackMessage(versionNumber, changelogContent);
20
20
  });
21
21
 
22
- function parseReleaseVersion(fileContent) {
23
- const regex = /##\s\[(\d+\.\d+.\d+)\]/;
24
- const releaseVersion = fileContent.match(regex);
25
-
26
- return releaseVersion[1];
27
- }
28
22
 
29
23
  function parseChangelogEntry(fileContent) {
30
24
  // The regex looks for the first paragraph starting with "###" until it finds
@@ -39,20 +33,53 @@ function parseChangelogEntry(fileContent) {
39
33
  }
40
34
 
41
35
  function sendSlackMessage(releaseVersion, changelogContent) {
42
- let message;
43
- let slackChannelId;
36
+ const slackChannelId = jobStatus === 'success' ? successSlackChannelId : failureSlackChannelId;
37
+ const generalPayload = {
38
+ channel: slackChannelId
39
+ };
40
+
41
+ let payload;
44
42
  if (jobStatus === 'success') {
45
- slackChannelId = successSlackChannelId
46
- message = `Changelog v${releaseVersion}\n${changelogContent}`
43
+ payload = {
44
+ ...generalPayload,
45
+ text: `New Bitmovin Player UI version is released!`,
46
+ attachments: [
47
+ {
48
+ title: `CHANGELOG v${releaseVersion}`,
49
+ color: '#0e7aff',
50
+ fallback: 'Changelog of the newest release should be displayed here',
51
+ text: changelogContent,
52
+ fields: [
53
+ {
54
+ title: 'Version',
55
+ value: `v${releaseVersion}`,
56
+ short: true,
57
+ },
58
+ {
59
+ title: 'Channel',
60
+ value: releaseVersion.includes('-') ? 'pre-release' : 'release',
61
+ short: true,
62
+ },
63
+ ],
64
+ },
65
+ ],
66
+ }
47
67
  } else {
48
- slackChannelId = failureSlackChannelId
49
- message = `Release v${releaseVersion} failed.\nPlease check https://github.com/bitmovin/bitmovin-player-ui/actions/runs/${runId}`
68
+ payload = {
69
+ ...generalPayload,
70
+ text: `<!subteam^S06RHTF937F> Release *v${releaseVersion}* failed.`,
71
+ attachments: [
72
+ {
73
+ title: `Release Failure`,
74
+ color: '#ff0000',
75
+ fallback: 'Release failed',
76
+ text: `Please check the <https://github.com/bitmovin/bitmovin-player-ui/actions/runs/${runId}|failed run>`,
77
+ },
78
+ ],
79
+ }
50
80
  }
51
81
 
52
- const sampleData = JSON.stringify({
53
- "channel": slackChannelId,
54
- "message": message
55
- });
82
+ const sampleData = JSON.stringify(payload);
56
83
  const options = {
57
84
  method: 'POST',
58
85
  headers: {
@@ -5,51 +5,51 @@ on:
5
5
  branches:
6
6
  - '**'
7
7
 
8
- workflow_dispatch:
9
-
10
8
  workflow_call:
11
9
 
10
+ concurrency:
11
+ group: ${{ github.event_name == 'workflow_call' && format('release-ci-{0}', github.run_id) || format('ci-{0}', github.ref) }}
12
+ cancel-in-progress: true
13
+
12
14
  jobs:
13
- test_and_build:
15
+ test:
16
+ name: Test
14
17
  runs-on: ubuntu-latest
15
18
 
16
19
  steps:
17
- # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
18
20
  - name: Checkout
19
21
  uses: actions/checkout@v4
20
22
 
21
- - name: Set up node.js
22
- uses: actions/setup-node@v4
23
- with:
24
- node-version-file: .nvmrc
25
-
26
- - name: Cache node modules
27
- id: cache-nodemodules
28
- uses: actions/cache@v4
29
- env:
30
- cache-name: cache-node-modules
31
- with:
32
- path: node_modules
33
- key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
34
-
35
- - name: Install dependencies
36
- if: steps.cache-nodemodules.outputs.cache-hit != 'true'
37
- run: npm ci
23
+ - name: Setup dependencies
24
+ uses: ./.github/actions/setup-dependencies
38
25
 
39
26
  - name: Test
40
27
  run: npm test
41
28
 
42
- - name: Build and prepare for a potential 'npm publish'
43
- run: gulp npm-prepare
29
+ build:
30
+ name: Build
31
+ runs-on: ubuntu-latest
32
+
33
+ steps:
34
+ - name: Checkout
35
+ uses: actions/checkout@v4
36
+
37
+ - name: Setup dependencies
38
+ uses: ./.github/actions/setup-dependencies
39
+
40
+ - name: Build
41
+ run: npx gulp build
42
+
43
+ lint:
44
+ name: Lint
45
+ runs-on: ubuntu-latest
44
46
 
45
- - name: Package artifact for upload
46
- run: tar -czvf artifact.tar.gz dist
47
- shell: bash
47
+ steps:
48
+ - name: Checkout
49
+ uses: actions/checkout@v4
48
50
 
49
- - uses: actions/upload-artifact@v4
50
- with:
51
- path: |
52
- ${{ github.workspace }}/artifact.tar.gz
53
- if-no-files-found: error
54
- retention-days: 1
51
+ - name: Setup dependencies
52
+ uses: ./.github/actions/setup-dependencies
55
53
 
54
+ - name: Lint
55
+ run: npx gulp lint
@@ -1,84 +1,238 @@
1
1
  name: Release
2
-
3
- on:
4
- push:
5
- tags:
6
- - '!player/'
7
- - 'v*'
2
+ run-name: Release ${{ inputs.version_number }} ${{ inputs.release_type }}${{ inputs.dry_run && ' (dry run)' || '' }}
3
+
4
+ on:
5
+ # For manually triggered workflows
6
+ workflow_dispatch:
7
+ inputs:
8
+ version_number:
9
+ description: "Version number of the release (no `v` prefix and no `-rc.`, `-beta.`, `-alpha.` suffix)"
10
+ type: string
11
+ required: true
12
+ release_type:
13
+ description: Select the type of the release
14
+ type: choice
15
+ options:
16
+ - alpha
17
+ - beta
18
+ - rc
19
+ - final
20
+ required: true
21
+ dry_run:
22
+ description: 'Run in dry run mode (skip commits, publishing, docs, and notifications)'
23
+ type: boolean
24
+ default: false
25
+
26
+ # For being called by other workflows
27
+ workflow_call:
28
+ inputs:
29
+ version_number:
30
+ description: "Version number of the release (no `v` prefix and no `-rc.`, `-beta.`, `-alpha.` suffix)"
31
+ type: string
32
+ required: true
33
+ release_type:
34
+ description: Select the type of the release
35
+ type: string
36
+ required: true
37
+ branch_name:
38
+ description: Branch from which the release should be built. Useful when another workflow calls this one.
39
+ type: string
40
+ required: true
41
+ dry_run:
42
+ description: 'Run in dry run mode (skip commits, publishing, docs, and notifications)'
43
+ type: boolean
44
+ default: false
45
+ secrets:
46
+ NPM_AUTH_TOKEN:
47
+ description: 'NPM authentication token for publishing'
48
+ required: true
49
+ GCS_CREDENTIALS:
50
+ description: 'Google Cloud Storage credentials for uploading docs'
51
+ required: true
52
+ SLACK_WEBHOOK_URL:
53
+ description: 'Slack webhook URL for notifications'
54
+ required: true
55
+ RELEASE_DEPLOY_KEY:
56
+ description: 'SSH deploy key with write access to the repository'
57
+ required: true
58
+
59
+ concurrency:
60
+ group: release-${{ inputs.version_number }}-${{ inputs.release_type }}
61
+ cancel-in-progress: true
8
62
 
9
63
  jobs:
10
- test_and_build:
11
- uses: ./.github/workflows/ci.yml
12
-
13
- download_and_publish:
14
- needs: test_and_build
64
+ version_bump:
65
+ name: Version Bump
15
66
  runs-on: ubuntu-latest
16
-
67
+ outputs:
68
+ major_version: ${{ steps.version_number.outputs.major_version }}
69
+ version_number: ${{ steps.version_number.outputs.version_number }}
70
+ tag_name: ${{ steps.version_number.outputs.tag_name }}
17
71
  steps:
18
- - name: Checkout
19
- uses: actions/checkout@v4
72
+ - uses: actions/checkout@v4
20
73
  with:
21
- ref: develop
22
-
23
- - name: Install dependencies
24
- run: npm ci
74
+ ssh-key: ${{ secrets.RELEASE_DEPLOY_KEY }}
75
+ ref: ${{ inputs.branch_name || github.ref }}
76
+ # Fetch existing tags because we need to create a new one
77
+ # fetch-tags: true # do not use, currently broken: https://github.com/actions/checkout/issues/1781
78
+ fetch-depth: 0 # remove this line when `fetch-tags: true` is fixed
79
+
80
+ - name: Setup dependencies
81
+ uses: ./.github/actions/setup-dependencies
82
+
83
+ - name: Get latest pre-release tag
84
+ id: latest_tag
85
+ if: inputs.release_type != 'final'
86
+ run: |
87
+ preReleaseTag="${{ inputs.release_type == 'final' && '' || inputs.release_type }}"
88
+ latest_tag=$(git tag --list "v${{ inputs.version_number }}-$preReleaseTag.*" | sort -V -r | head -n1 || echo "")
89
+ echo "latest_tag=$latest_tag" >> $GITHUB_OUTPUT
90
+
91
+ - name: Calculate version number
92
+ uses: actions/github-script@v6
93
+ id: version_number
94
+ with:
95
+ script: |
96
+ const { calculateVersionNumber } = require('./.github/scripts/calculateVersionNumber.js');
97
+
98
+ const inputVersionNumber = '${{ inputs.version_number }}';
99
+ const releaseType = '${{ inputs.release_type }}';
100
+ const latestTag = '${{ steps.latest_tag.outputs.latest_tag }}';
101
+
102
+ try {
103
+ const result = calculateVersionNumber(core, inputVersionNumber, releaseType, latestTag);
104
+
105
+ core.setOutput('version_number', result.version_number);
106
+ core.setOutput('major_version', result.major_version);
107
+ core.setOutput('tag_name', result.tag_name);
108
+ } catch (error) {
109
+ core.setFailed(error.message);
110
+ }
111
+
112
+ - name: Bump version in package.json
113
+ run: |
114
+ npm version "${{ steps.version_number.outputs.version_number }}" --no-git-tag-version
115
+ shell: bash
25
116
 
26
- - name: Read package.json version
117
+ - name: Update changelog
118
+ if: inputs.release_type == 'final'
27
119
  uses: actions/github-script@v6
28
- id: extract-version
29
120
  with:
30
121
  script: |
31
- const { version } = require('./package.json')
32
- core.setOutput('packageJsonVersion', version)
122
+ const fs = require('fs');
123
+ const { updateChangeLog } = require('./.github/scripts/updateChangelog.js');
124
+
125
+ const versionNumber = '${{ steps.version_number.outputs.version_number }}';
126
+ const releaseDate = new Date().toISOString().split('T')[0];
127
+
128
+ core.info(`Updating changelog with version ${versionNumber} and date ${releaseDate}`);
129
+
130
+ const changelogContent = fs.readFileSync('./CHANGELOG.md', { encoding: 'utf8', flag: 'r' });
131
+ const updatedChangelog = updateChangeLog(changelogContent, versionNumber, releaseDate);
132
+
133
+ fs.writeFileSync('./CHANGELOG.md', updatedChangelog, 'utf-8');
134
+ core.info('Changelog updated successfully');
135
+
136
+ - name: Commit and tag the changes
137
+ if: inputs.dry_run != true
138
+ run: |
139
+ git config --global user.name 'Automated Release'
140
+ git config --global user.email 'release-automation@bitmovin.com'
141
+ git add package.json CHANGELOG.md
142
+ git commit -m "Bump version to ${{ steps.version_number.outputs.version_number }}"
143
+ git tag "${{ steps.version_number.outputs.tag_name }}"
144
+ git push origin ${{ inputs.branch_name || github.ref_name }}
145
+ git push origin "${{ steps.version_number.outputs.tag_name }}"
146
+
147
+ test:
148
+ needs: version_bump
149
+ name: Test, Build and Lint
150
+ uses: ./.github/workflows/ci.yml
33
151
 
34
- - uses: actions/download-artifact@v4
152
+ release:
153
+ name: Publish Release
154
+ needs: [test, version_bump]
155
+ runs-on: ubuntu-latest
156
+ steps:
157
+ - name: Checkout
158
+ uses: actions/checkout@v4
35
159
  with:
36
- path: .
160
+ ref: ${{ inputs.dry_run == true && github.ref || needs.version_bump.outputs.tag_name }}
161
+
162
+ - name: Setup dependencies
163
+ uses: ./.github/actions/setup-dependencies
37
164
 
38
- - name: Unpackage artifact files
39
- run: tar -xzvf artifact/artifact.tar.gz -C .
165
+ - name: Build Release
166
+ run: npx gulp npm-prepare
40
167
  shell: bash
41
168
 
42
- - name: Publish
43
- run: ./publish.sh
169
+ - name: Publish to NPM
170
+ run: ./publish.sh --version "${{ needs.version_bump.outputs.version_number }}" ${{ inputs.dry_run == true && '--dry-run' || '' }}
44
171
  shell: bash
45
172
  env:
46
- NPM_DRY_RUN: false
47
173
  NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
48
174
 
175
+ upload_docs:
176
+ name: Build and upload Documentation
177
+ needs: [test, version_bump, release]
178
+ runs-on: ubuntu-latest
179
+ steps:
180
+ - name: Checkout
181
+ uses: actions/checkout@v4
182
+ with:
183
+ ref: ${{ inputs.dry_run == true && github.ref || needs.version_bump.outputs.tag_name }}
184
+
185
+ - name: Setup dependencies
186
+ uses: ./.github/actions/setup-dependencies
187
+
49
188
  - name: Build documentation
50
189
  run: npx typedoc
51
190
  shell: bash
52
191
 
53
192
  - name: Authenticate
193
+ if: inputs.dry_run != true
54
194
  uses: 'google-github-actions/auth@v2'
55
195
  with:
56
196
  credentials_json: ${{ secrets.GCS_CREDENTIALS }}
57
197
 
58
198
  - name: Upload docs
199
+ if: inputs.dry_run != true
59
200
  uses: 'google-github-actions/upload-cloud-storage@v2'
60
201
  with:
61
202
  path: './docs/'
62
- destination: "${{ secrets.GCS_BUCKET }}/player/ui/${{ steps.extract-version.outputs.packageJsonVersion }}"
203
+ destination: "${{ secrets.GCS_BUCKET }}/player/ui/${{ needs.version_bump.outputs.version_number }}"
63
204
 
64
205
  - name: Upload docs for major version
206
+ if: inputs.dry_run != true && inputs.release_type == 'final'
65
207
  uses: 'google-github-actions/upload-cloud-storage@v2'
66
208
  with:
67
209
  path: './docs/'
68
- destination: "${{ secrets.GCS_BUCKET }}/player/ui/3"
210
+ destination: "${{ secrets.GCS_BUCKET }}/player/ui/${{ needs.version_bump.outputs.major_version }}"
211
+
212
+ notify_success:
213
+ name: Notify Success
214
+ needs: [test, version_bump, release, upload_docs]
215
+ runs-on: ubuntu-latest
216
+ if: inputs.dry_run != true
217
+ steps:
218
+ - name: Checkout
219
+ uses: actions/checkout@v4
220
+ with:
221
+ ref: ${{ needs.version_bump.outputs.tag_name }}
69
222
 
70
223
  - name: Notify team
71
- run: node .github/scripts/notifySlackTeam.js 'success' 'CHANGELOG.md' ${{ secrets.RELEASE_SUCCESS_SLACK_WEBHOOK }}
224
+ run: node .github/scripts/notifySlackTeam.js ${{ needs.version_bump.outputs.version_number }} 'success' 'CHANGELOG.md' ${{ secrets.SLACK_WEBHOOK_URL }}
72
225
 
73
226
  handle_failure:
227
+ name: Handle Failure
74
228
  runs-on: ubuntu-latest
75
- needs: [test_and_build, download_and_publish]
76
- if: ${{ always() && (needs.download_and_publish.result == 'failure' || needs.test_and_build.result == 'failure') }}
229
+ needs: [test, version_bump, release, upload_docs, notify_success]
230
+ if: ${{ always() && inputs.dry_run != true && (needs.test.result == 'failure' || needs.version_bump.result == 'failure' || needs.release.result == 'failure' || needs.upload_docs.result == 'failure' || needs.notify_success.result == 'failure') }}
77
231
  steps:
78
232
  - name: Checkout
79
233
  uses: actions/checkout@v4
80
234
  with:
81
- ref: develop
235
+ ref: ${{ needs.version_bump.outputs.tag_name }}
82
236
 
83
237
  - name: Notify team
84
- run: node .github/scripts/notifySlackTeam.js 'failure' 'CHANGELOG.md' ${{ secrets.RELEASE_FAILURE_SLACK_WEBHOOK }} ${{ github.run_id }}
238
+ run: node .github/scripts/notifySlackTeam.js ${{ needs.version_bump.outputs.version_number }} 'failure' 'CHANGELOG.md' ${{ secrets.SLACK_WEBHOOK_URL }} ${{ github.run_id }}