diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 049b905..ea0a90d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,5 +27,10 @@ jobs: with: node-version: "22" architecture: 'x64' # fix for macos-latest - - run: npm ci + registry-url: 'https://npm.pkg.github.com' + scope: '@plexinc' + - name: npm ci + env: + NODE_AUTH_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + run: npm ci - run: npm run preversion diff --git a/.github/workflows/xelp_npm_release.yml b/.github/workflows/xelp_npm_release.yml new file mode 100644 index 0000000..6e91c6f --- /dev/null +++ b/.github/workflows/xelp_npm_release.yml @@ -0,0 +1,191 @@ +name: Xelp npm Release + +# Builds the current xelp/main, publishes it to GitHub Packages as +# @plexinc/, then tags the commit and creates a GitHub release. +# +# Authentication is the workflow's own GITHUB_TOKEN, so there is no secret to +# provision or rotate. Consumers authenticate the way every other Plex client +# repo does, with a personal access token carrying read:packages. +# +# This replaces xelp_shadow_release.yml, which committed dist/ to the xelp/dist +# branch and served the package to consumers through a git tag. Run one or the +# other, never both: they compute the same version string and so want the same +# tag, and the shadow release force pushes it. +# +# Keep the shadow release around until roku-client, the only consumer of these +# forks, is installing from the @plexinc package. Then delete it. +# + +on: + workflow_dispatch: + inputs: + dryRun: + description: Build and pack, but do not publish, tag, or release. + type: boolean + default: false + +# packages: write is the publish permission. contents: write is only for the +# tag and the release. +# +permissions: + contents: write + packages: write + +jobs: + release: + runs-on: blacksmith-2vcpu-ubuntu-2404 + steps: + - name: Check out xelp/main + uses: actions/checkout@v5 + with: + ref: xelp/main + fetch-depth: 0 + + - name: Set up Node.js + uses: actions/setup-node@v5 + with: + node-version: 20 + registry-url: https://npm.pkg.github.com + scope: '@plexinc' + + - name: Configure git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Work out the package name and version + id: release + run: | + set -euo pipefail + + UPSTREAM_NAME=$(node -p "require('./package.json').name") + + # Re-scope to @plexinc, dropping any existing scope, so + # @rokucommunity/bslint becomes @plexinc/bslint. GitHub Packages + # requires the scope to match the owner of this repository. + # + SCOPED_NAME="@plexinc/${UPSTREAM_NAME##*/}" + + # Version scheme, unchanged from the shadow release: the upstream major + # and minor, then the build date with the upstream patch appended, so + # 0.70.3 built on 2026-08-28 becomes 0.70.202608283. + # + CURRENT_VERSION=$(node -p "require('./package.json').version") + BASE_VERSION=${CURRENT_VERSION%%[-+]*} + + IFS='.' read -r -a PARTS <<< "$BASE_VERSION" + if [ ${#PARTS[@]} -ne 3 ]; then + echo "::error::Version $CURRENT_VERSION is not MAJOR.MINOR.PATCH" + exit 1 + fi + + VERSION="${PARTS[0]}.${PARTS[1]}.$(date -u +'%Y%m%d')${PARTS[2]}" + + { + echo "upstream_name=$UPSTREAM_NAME" + echo "scoped_name=$SCOPED_NAME" + echo "version=$VERSION" + echo "metadata_version=$VERSION+xelp-$(git rev-parse --short HEAD)" + } >> "$GITHUB_OUTPUT" + + # Published versions are immutable and tags are no longer force-pushed, so + # a same day re-run would collide twice over. Fail before doing the work. + # + - name: Fail if this version is already published + env: + # Only the three steps that talk to the registry get the token: this + # one, the install, and the publish. Building, linting, testing and + # packing do not reach the network, so they do not need it. Add it to + # any new step that runs npm against the registry. + # + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SCOPED_NAME: ${{ steps.release.outputs.scoped_name }} + VERSION: ${{ steps.release.outputs.version }} + run: | + if npm view "$SCOPED_NAME@$VERSION" version >/dev/null 2>&1; then + echo "::error::$SCOPED_NAME@$VERSION is already published. Land another commit, or wait for tomorrow's date stamp." + exit 1 + fi + + # The upstream lockfile is what keeps the transitive dependencies on + # working versions, so install from it rather than re-resolving. A clean + # re-resolve floats vscode-languageserver-protocol onto an exports only + # release that the TypeScript build cannot import. + # + - name: Install dependencies + env: + # Dependencies can live in another repo's package, and a repository's + # own GITHUB_TOKEN cannot read those, so prefer the organization token. + # The fallback keeps this working in a fork that has no @plexinc + # dependencies, where the repo token is enough. + # + NODE_AUTH_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + run: npm ci + + - name: Build + run: npm run build + + # The shadow release ran the lint and test suites as a side effect of + # npm version, which triggers the preversion script. Setting the version + # through npm pkg set does not run lifecycle scripts, so the gate runs + # here where a failure names the step that failed. + # + - name: Lint + run: npm run lint + + - name: Test + run: npm test + + # The rename happens here and is never committed to xelp/main, so a merge + # from upstream never has to resolve a changed package name. The repository + # URL is what links the package to this repo, and GitHub Packages rejects + # the publish if it points anywhere else. + # + - name: Rewrite the package metadata for the @plexinc scope + env: + SCOPED_NAME: ${{ steps.release.outputs.scoped_name }} + VERSION: ${{ steps.release.outputs.version }} + run: | + npm pkg set name="$SCOPED_NAME" + npm pkg set version="$VERSION" + npm pkg set repository.url="git+https://github.com/${{ github.repository }}.git" + + - name: Publish to GitHub Packages + if: ${{ !inputs.dryRun }} + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: npm publish + + - name: Pack without publishing + if: ${{ inputs.dryRun }} + run: npm pack --dry-run + + - name: Tag the release + if: ${{ !inputs.dryRun }} + env: + VERSION: ${{ steps.release.outputs.version }} + METADATA_VERSION: ${{ steps.release.outputs.metadata_version }} + run: | + git tag -a "$VERSION" -m "Release $METADATA_VERSION" + git push origin "$VERSION" + + - name: Create the GitHub release + if: ${{ !inputs.dryRun }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + UPSTREAM_NAME: ${{ steps.release.outputs.upstream_name }} + SCOPED_NAME: ${{ steps.release.outputs.scoped_name }} + VERSION: ${{ steps.release.outputs.version }} + METADATA_VERSION: ${{ steps.release.outputs.metadata_version }} + run: | + gh release create "$VERSION" \ + --repo "$GITHUB_REPOSITORY" \ + --title "$SCOPED_NAME $VERSION" \ + --notes "Built from \`$METADATA_VERSION\`. + + Consume it with an alias, so the package keeps its upstream name inside \`node_modules\`: + + \`\`\`json + \"$UPSTREAM_NAME\": \"npm:$SCOPED_NAME@$VERSION\" + \`\`\`" \ + --prerelease diff --git a/.github/workflows/xelp_shadow_release.yml b/.github/workflows/xelp_shadow_release.yml.disabled similarity index 100% rename from .github/workflows/xelp_shadow_release.yml rename to .github/workflows/xelp_shadow_release.yml.disabled diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..3ccf31e --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +# Use GitHub for @plexinc packages. +@plexinc:registry=https://npm.pkg.github.com/ +//npm.pkg.github.com/:always-auth=true diff --git a/package-lock.json b/package-lock.json index 57e74b5..44a390f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.7.21", "license": "MIT", "dependencies": { - "brighterscript": "https://github.com/plexinc/brighterscript.git#0.70.202603123", + "brighterscript": "npm:@plexinc/brighterscript@^0.70.0", "glob-all": "^3.3.0", "jsonc-parser": "^3.0.0", "source-map": "0.7.4", @@ -1559,8 +1559,10 @@ } }, "node_modules/brighterscript": { - "version": "0.70.202603123", - "resolved": "git+ssh://git@github.com/plexinc/brighterscript.git#b1c0191837cb61b938015f9ba086348820513f97", + "name": "@plexinc/brighterscript", + "version": "0.70.202608283", + "resolved": "https://npm.pkg.github.com/download/@plexinc/brighterscript/0.70.202608283/f0a4be489585bc03e84d8e3a012494f4872fc347", + "integrity": "sha512-ixJVQQmh1UO1th6/0p8Sh9yg1hXctWyK328o5Oi0gi1UhtuWIUftrwtLXRks3ZSpSAgf+Wsb+XoRY65N79cDeQ==", "license": "MIT", "dependencies": { "@rokucommunity/bslib": "^0.1.1", diff --git a/package.json b/package.json index 7a08da2..5c2d012 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "brighterscript-formatter": "dist/cli.js" }, "dependencies": { - "brighterscript": "https://github.com/plexinc/brighterscript.git#0.70.202603123", + "brighterscript": "npm:@plexinc/brighterscript@^0.70.0", "glob-all": "^3.3.0", "jsonc-parser": "^3.0.0", "source-map": "0.7.4",