Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
version: 2
updates:
- package-ecosystem: npm
directory: '/'
schedule:
interval: weekly
day: monday
time: '07:00'
open-pull-requests-limit: 5
commit-message:
prefix: 'chore(deps)'
prefix-development: 'chore(deps-dev)'
include: scope
labels:
- dependencies
groups:
# Everything here is a devDependency, so batch the routine bumps into one
# PR per week instead of five. Major bumps stay separate so tooling
# upgrades like ESLint or Vitest get reviewed on their own.
dev-minor-and-patch:
dependency-type: development
update-types:
- minor
- patch

- package-ecosystem: github-actions
directory: '/'
schedule:
interval: weekly
day: monday
time: '07:00'
open-pull-requests-limit: 5
commit-message:
prefix: 'chore(ci)'
include: scope
labels:
- dependencies
- github-actions
groups:
actions:
patterns:
- '*'
47 changes: 45 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,69 @@ name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

# The default GITHUB_TOKEN is granted no more than read access to the repo.
permissions:
contents: read

Comment thread
qodo-code-review[bot] marked this conversation as resolved.
# A new push to a branch cancels any CI run still in flight for that branch.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
name: Node ${{ matrix.node-version }}
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
node-version: [20.x, 22.x, 24.x]

steps:
- uses: actions/checkout@v4
# Third-party actions are pinned by commit SHA rather than tag, so a
# compromised or retargeted tag cannot silently change what runs here.
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'

- run: npm ci
- run: npm run lint
- run: npm run typecheck
- run: npm run build
- run: npm test

audit:
name: Dependency audit
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Use Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24.x
cache: 'npm'

- run: npm ci

# Fails the job on a high or critical advisory. Everything this package
# depends on is a devDependency, so a finding here is a build-chain risk
# rather than something shipped to consumers, but it should still block.
- name: npm audit
run: npm audit --audit-level=high
97 changes: 97 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: Release

# Publishes to npm when a GitHub Release is published, or on manual dispatch.
#
# Authentication is via npm trusted publishing (OIDC) — there is no NPM_TOKEN
# secret and nothing to rotate. The trusted publisher is registered on npm
# against this repo, this workflow filename, and the `npm` environment below;
# all four must keep matching or the publish will fail to authenticate.
on:
release:
types: [published]
workflow_dispatch:
inputs:
# Underscore, not a hyphen: Actions expressions parse `inputs.dry-run`
# as subtraction, so a hyphenated input name is unreferenceable via dot
# notation.
dry_run:
description: 'Run every step but skip the actual npm publish'
type: boolean
default: true

permissions:
contents: read

jobs:
publish:
name: Publish to npm
runs-on: ubuntu-latest

# `id-token: write` is what mints the OIDC token npm exchanges for publish
# rights, and what binds the provenance attestation to this workflow and
# commit. Without it there is no way to authenticate at all.
permissions:
contents: read
id-token: write

environment:
name: npm
url: https://www.npmjs.com/package/youtube-transcript-plus

steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Use Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24.x
cache: 'npm'
registry-url: 'https://registry.npmjs.org'

- run: npm ci

# Re-run the full gate here rather than trusting that CI passed on the
# commit — this is the last point before an immutable publish.
- run: npm run lint
- run: npm run typecheck
- run: npm test
- run: npm run build

# Guards against tagging a release whose package.json was never bumped.
- name: Verify tag matches package.json version
if: github.event_name == 'release'
run: |
PKG_VERSION="$(node -p "require('./package.json').version")"
TAG_VERSION="${GITHUB_REF_NAME#v}"
echo "package.json: $PKG_VERSION"
echo "git tag: $TAG_VERSION"
if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then
echo "::error::Tag $GITHUB_REF_NAME does not match package.json version $PKG_VERSION"
exit 1
fi

- name: Verify version is not already published
run: |
PKG_VERSION="$(node -p "require('./package.json').version")"
if npm view "youtube-transcript-plus@$PKG_VERSION" version >/dev/null 2>&1; then
echo "::error::Version $PKG_VERSION is already published to npm"
exit 1
fi

- name: Show tarball contents
run: npm pack --dry-run

# No auth env: the npm CLI detects the Actions OIDC environment and
# exchanges it for short-lived publish credentials on its own. Provenance
# is generated automatically under trusted publishing, but the flag is
# kept so the attestation is requested explicitly rather than implied.
- name: Publish
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && !inputs.dry_run)
run: npm publish --provenance

- name: Dry run (no publish)
if: github.event_name == 'workflow_dispatch' && inputs.dry_run
run: echo "Dry run complete. Re-run with dry_run unchecked to publish."
55 changes: 55 additions & 0 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Scorecard

# OpenSSF Scorecard grades this repo's supply-chain posture (pinned actions,
# token permissions, branch protection, published provenance, and so on) and
# uploads the result to GitHub code scanning.
on:
branch_protection_rule:
schedule:
# Weekly, Monday 07:00 UTC.
- cron: '0 7 * * 1'
push:
branches: [main]
workflow_dispatch:

permissions: read-all

jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest

permissions:
# Required to upload the SARIF result to code scanning.
security-events: write
# Required by the publish_results option below.
id-token: write
contents: read
actions: read

steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Run analysis
uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
with:
results_file: results.sarif
results_format: sarif
# Publishes the score to the OpenSSF API, which is what backs the
# public badge in the README.
publish_results: true

- name: Upload artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: scorecard-results
path: results.sarif
retention-days: 5

- name: Upload to code scanning
uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4
with:
sarif_file: results.sarif
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# youtube-transcript-plus

[![npm version](https://badge.fury.io/js/youtube-transcript-plus.svg)](https://badge.fury.io/js/youtube-transcript-plus)
[![CI](https://github.com/ericmmartin/youtube-transcript-plus/actions/workflows/ci.yml/badge.svg)](https://github.com/ericmmartin/youtube-transcript-plus/actions/workflows/ci.yml)
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/ericmmartin/youtube-transcript-plus/badge)](https://scorecard.dev/viewer/?uri=github.com/ericmmartin/youtube-transcript-plus)

A Node.js library to fetch transcripts from YouTube videos. This package uses YouTube's unofficial API, so it may break if YouTube changes its internal structure.

Expand Down Expand Up @@ -394,6 +396,17 @@ The library throws the following errors:
- **`YoutubeTranscriptInvalidVideoIdError`**: The provided video ID or URL is invalid.
- **`YoutubeTranscriptInvalidLangError`**: The provided language code is not a valid BCP 47 code. Properties: `lang`.

## Supply Chain

Releases are published to npm from a GitHub Actions workflow with
[npm provenance](https://docs.npmjs.com/generating-provenance-statements), so each
published tarball is cryptographically linked to the commit and workflow run that
built it. You can verify the signatures of your installed dependencies with:

```bash
npm audit signatures
```

## Feature Requests

Have a feature idea? [Open an issue](https://github.com/ericmmartin/youtube-transcript-plus/issues/new) and let us know!
Expand Down