Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
cfef20c
FLEXY-5531 dual webpack changes
shyamasish-twilio Jun 11, 2025
801d4e1
Merge branch 'main' into FLEXY-5531
shyamasish-twilio Sep 8, 2026
eea265d
FLEXY-5531 added fixes for wp5
shyamasish-twilio Sep 9, 2026
4a8f4e6
FLEXY-5531 added unit test fixes for wp5
shyamasish-twilio Sep 9, 2026
1b4521f
FLEXY-6498 add Node 24 / webpack5 e2e suite alongside existing Node 2…
shyamasish-twilio Sep 23, 2026
64ae7a9
FLEXY-6498 stabilize @types/node resolution against unstable Definite…
shyamasish-twilio Sep 23, 2026
f3e2b54
FLEXY-6498 pin all GitHub Actions to full commit SHAs to satisfy org …
shyamasish-twilio Sep 23, 2026
1f503ec
FLEXY-6498 pin codecov-action to an org-approved SHA (v4.0.2)
shyamasish-twilio Sep 23, 2026
0014e11
FLEXY-6498 remove Codecov reporting step to unblock PR checks
shyamasish-twilio Sep 23, 2026
015d7c6
FLEXY-6498 use ubuntu-x64 runner label to match flex-monorepo
shyamasish-twilio Sep 23, 2026
f6c1dfd
FLEXY-6498 wire up Artifactory OIDC auth and adopt lockdown-migration…
shyamasish-twilio Sep 23, 2026
857e525
FLEXY-6498 resolve lockfile against public npm and add lockfile hygie…
shyamasish-twilio Sep 23, 2026
8410d3a
FLEXY-6498 override curation-blocked deps, fail CI on npm ci failure,…
shyamasish-twilio Sep 23, 2026
506f309
FLEXY-6498 pin ejs 6.0.1 / rfc6902 5.3.0, add full curation probe
shyamasish-twilio Sep 23, 2026
03bb083
FLEXY-6498 pin nested axios to 0.26.1, fix webpack5 config array typing
shyamasish-twilio Sep 23, 2026
9496cff
FLEXY-6498 add webpack 5 build/start tests to restore flex-plugin-scr…
shyamasish-twilio Sep 23, 2026
66382eb
FLEXY-6498 set always-auth for yarn, use macos-latest, drop curation …
shyamasish-twilio Sep 23, 2026
8114b40
FLEXY-6498 e2e: pin ejs to 6.x for twilio plugins:install
shyamasish-twilio Sep 23, 2026
f9f15af
FLEXY-6498 e2e: temporary curation probe for plugins:install and crea…
shyamasish-twilio Sep 23, 2026
4aca282
FLEXY-6498 e2e: pin undici to 6.x for plugins:install, drop curation …
shyamasish-twilio Sep 23, 2026
6211b38
FLEXY-6498 drop redundant overrides, probe curated repo for parent up…
shyamasish-twilio Sep 25, 2026
0b00b1f
FLEXY-6498 rewrite Artifactory lockfile URLs in a pre-commit hook
shyamasish-twilio Sep 27, 2026
2ce1218
FLEXY-6498 remove Flex UI 1.x and upgrade TypeScript to 4.9
shyamasish-twilio Sep 27, 2026
d4a969b
FLEXY-6498 re-authenticate with Artifactory before running tests
shyamasish-twilio Sep 27, 2026
48897c2
FLEXY-6498 re-authenticate with Artifactory before each e2e invocation
shyamasish-twilio Sep 27, 2026
300cfd1
FLEXY-6498 run the e2e pipeline against the public npm registry
shyamasish-twilio Sep 28, 2026
78c2443
FLEXY-6498 drop the CLI data dir pin and fix the remove-label jobs
shyamasish-twilio Sep 28, 2026
d02b75a
FLEXY-6498 pin @types/node in the generated plugin templates
shyamasish-twilio Sep 28, 2026
329ad3b
FLEXY-6498 align engines to ^18 || ^20 || ^22 || ^24 across packages
shyamasish-twilio Sep 29, 2026
0c5ddf0
FLEXY-6498 only remove the e2e label when e2e actually failed
shyamasish-twilio Sep 29, 2026
2cb4549
FLEXY-6498 run the webpack 5 e2e on the run-e2e label
shyamasish-twilio Sep 29, 2026
c26f4f3
Merge main into FLEXY-6498
shyamasish-twilio Sep 30, 2026
89b002c
FLEXY-6498 run the webpack 5 e2e on Node 22 for now
shyamasish-twilio Oct 6, 2026
bb5b1bb
Merge branch 'main' into FLEXY-6498
shyamasish-twilio Oct 6, 2026
a5b1500
FLEXY-6498 remove the flex-plugin-scripts dependency (from #1151)
shyamasish-twilio Oct 7, 2026
5eebb26
FLEXY-6498 use the latest Serverless build runtime
shyamasish-twilio Oct 7, 2026
87c1499
FLEXY-6498 run e2e.yml for webpack 4 and 5 on Node 22 and 24
shyamasish-twilio Oct 7, 2026
6f132e7
FLEXY-6498 check out the branch being released in publish.yml
shyamasish-twilio Oct 7, 2026
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
51 changes: 51 additions & 0 deletions .github/actions/artifactory-oidc/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: 'Artifactory OIDC Auth'
description: 'Exchange GitHub OIDC token for Artifactory access token and configure npm'

runs:
using: 'composite'
steps:
- name: Exchange OIDC token for Artifactory access token
shell: bash
run: |
# 1. Request GitHub JWT with Artifactory as audience
GITHUB_JWT=$(curl -sS \
-H "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \
"${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=${ARTIFACTORY_URL}" \
| jq -r '.value')

# Decode and log claims for debugging (payload is base64url, not secret)
PAYLOAD=$(echo "$GITHUB_JWT" | cut -d. -f2 | sed 's/-/+/g; s/_/\//g' | base64 -d 2>/dev/null || true)
echo "::debug::OIDC claims: sub=$(echo "$PAYLOAD" | jq -r '.sub'), aud=$(echo "$PAYLOAD" | jq -r '.aud')"

# 2. Exchange GitHub JWT for Artifactory access token.
# Must be a JSON body and MUST include provider_name so JFrog knows
# which OIDC configuration to match (without it, no token is issued).
# Capture the full response so JFrog's error is visible on failure.
RESP=$(curl -sS "${ARTIFACTORY_URL}/access/api/v1/oidc/token" \
-H 'Content-Type: application/json' \
-d "{\"grant_type\":\"urn:ietf:params:oauth:grant-type:token-exchange\",
\"subject_token_type\":\"urn:ietf:params:oauth:token-type:id_token\",
\"subject_token\":\"${GITHUB_JWT}\",
\"provider_name\":\"github-actions\"}")
ART_TOKEN=$(echo "$RESP" | jq -r '.access_token // empty')

if [ -z "$ART_TOKEN" ]; then
echo "::error::OIDC token exchange failed. Raw response (token field redacted):"
echo "$RESP" | jq 'if .access_token then .access_token="<redacted>" else . end' 2>/dev/null || echo "$RESP"
exit 1
fi
echo "::add-mask::$ART_TOKEN"

# 3. Configure npm to use Artifactory registry
# always-auth: yarn 1 (bundled in twilio-cli, used by `twilio plugins:install`
# in the e2e tests) only sends the _authToken when always-auth is set;
# without it Artifactory returns 401 and yarn reports "Couldn't find package".
REGISTRY="${ARTIFACTORY_URL}/artifactory/api/npm/virtual-npm-thirdparty/"
HOST=$(echo "${ARTIFACTORY_URL}" | sed -E 's#^https?://##')
{
echo "registry=${REGISTRY}"
echo "//${HOST}/artifactory/api/npm/virtual-npm-thirdparty/:_authToken=${ART_TOKEN}"
echo "always-auth=true"
} > ~/.npmrc

echo "::notice::npm configured to use Artifactory registry"
62 changes: 62 additions & 0 deletions .github/scripts/lockfile-hygiene.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
# Lockfile & resolver-config hygiene gate (ADR 1634 Rule 3b).
#
# Public repos MUST stay resolvable by external consumers: a committed
# lockfile / resolver config that names a Twilio Artifactory host breaks any
# external `npm ci`. Twilio resolution is redirected at build time by config
# (the OIDC action writes ~/.npmrc), never in the repo — so the committed files
# must always point at the public registry.
#
# This script DETECTS violations and fails closed. It does not rewrite files;
# remediation is to regenerate the lockfile against the public registry.
set -euo pipefail

# Known Twilio Artifactory hosts / aliases. Extend as new hosts appear.
PATTERNS='twilio\.jfrog\.io|artifactory\.twilio|artifacts\.twilio|\.artifactory\.twilioinfra|jfrog\.twilio'

# Closed, enumerated detection surface (ADR Rule 3b). npmrc files and every
# committed lockfile / shrinkwrap.
# Portable (no mapfile): newline-delimited, iterated with a while-read loop.
FILES="$(git ls-files | grep -E '(^|/)(package-lock\.json|npm-shrinkwrap\.json|\.npmrc)$' || true)"

if [ -z "$FILES" ]; then
echo "hygiene: no lockfiles or resolver configs tracked — nothing to check."
exit 0
fi

count="$(printf '%s\n' "$FILES" | grep -c .)"
echo "hygiene: scanning ${count} file(s) for Twilio Artifactory hosts..."
violations=0
while IFS= read -r f; do
[ -n "$f" ] || continue
if hits=$(grep -nEi "$PATTERNS" "$f" 2>/dev/null); then
violations=1
echo "::error file=$f::Artifactory host found in $f — public consumers cannot resolve this. Regenerate against the public registry."
echo " ── $f"
echo "$hits" | sed 's/^/ /' | head -8
fi
# npm-shrinkwrap.json ships inside the published tarball — extra-loud.
case "$f" in
*npm-shrinkwrap.json)
echo "::warning file=$f::npm-shrinkwrap.json is published in the tarball; any Artifactory host here reaches consumers." ;;
esac
done <<EOF
$FILES
EOF

if [ "$violations" -ne 0 ]; then
cat <<'EOF'

✗ Lockfile hygiene FAILED.
A committed lockfile / resolver config points at a Twilio Artifactory host.
External consumers cannot resolve against it.

Fix: regenerate the lockfile against the PUBLIC registry, e.g.
rm -f ~/.npmrc && npm_config_registry=https://registry.npmjs.org \
npm install --package-lock-only
then commit the cleaned lockfile.
EOF
exit 1
fi

echo "✓ Lockfile hygiene passed — all tracked files resolve against the public registry."
44 changes: 44 additions & 0 deletions .github/scripts/public-lockfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env node
/* eslint-disable no-console */
// Pre-commit counterpart to lockfile-hygiene.sh (ADR 1634 Rule 3b).
//
// Twilio resolution is redirected at build time by config (the OIDC action writes
// ~/.npmrc), never in the repo — so a lockfile generated on a machine pointed at
// Artifactory carries Artifactory URLs and breaks `npm ci` for external consumers.
// The CI gate only detects that and fails; this rewrites it at the source, so the
// committed lockfile always names the public registry.
//
// Rewrites the host+repo prefix only. The path after it (`/<pkg>/-/<file>.tgz`) is
// identical on both, and `integrity` is left alone because Artifactory proxies
// byte-identical tarballs. A package published ONLY to Artifactory cannot be
// rewritten safely — it would 404 publicly — and this cannot detect that offline;
// the clean-room install in CI is what catches it.

const { execFileSync } = require('child_process');
const fs = require('fs');

const ARTIFACTORY =
/https:\/\/[^/"]*(?:twilio\.jfrog\.io|jfrog\.twilio|artifacts\.twilio|artifactory\.twilio)[^/"]*\/artifactory\/api\/npm\/[^/"]+\//g;
const PUBLIC = 'https://registry.npmjs.org/';

const staged = execFileSync('git', ['diff', '--cached', '--name-only', '--diff-filter=ACM'], { encoding: 'utf8' })
.split('\n')
.filter((f) => /(^|\/)(package-lock\.json|npm-shrinkwrap\.json)$/.test(f));

if (!staged.length) process.exit(0);

let rewrote = false;
for (const file of staged) {
const before = fs.readFileSync(file, 'utf8');
const after = before.replace(ARTIFACTORY, PUBLIC);
if (before === after) continue;
const count = (before.match(ARTIFACTORY) || []).length;
fs.writeFileSync(file, after);
execFileSync('git', ['add', file]);
console.log(`lockfile: rewrote ${count} Artifactory URL(s) to the public registry in ${file}`);
rewrote = true;
}

if (rewrote) {
console.log('lockfile: re-staged. CI still runs a clean-room public install to verify it resolves.');
}
14 changes: 14 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ name: E2E
# freshly computed timestamp, so there was no way to point e2e at a particular
# alpha. This takes the version as an input.
#
# Runs the suite for every combination of webpack (4, 5) and Node (22, 24).
# max-parallel is 1 because each OS leg uses a single Twilio account, and two
# combinations against it at once would collide on plugins and releases.
# fail-fast is off so one failing combination does not hide the others.
#
# Like publish.yml, this has to exist on the default branch for GitHub to expose
# the workflow_dispatch trigger; a run can then target any branch.

Expand All @@ -29,12 +34,21 @@ on:

jobs:
e2e-test:
name: e2e (webpack ${{ matrix.webpack }}, node ${{ matrix.node }})
strategy:
fail-fast: false
max-parallel: 1
matrix:
webpack: [4, 5]
node: ['22', '24']
uses: ./.github/workflows/~reusable_e2e_all_OS.yaml
with:
BRANCH: ${{ inputs.BRANCH }}
NPM_IGNORE_PREFIX: ${{ vars.NPM_IGNORE_PREFIX }}
PACKAGE_VERSION: ${{ inputs.PACKAGE_VERSION }}
FLEX_UI_VERSION: ${{ inputs.FLEX_UI_VERSION }}
NODE_VERSION: ${{ matrix.node }}
WP5: ${{ matrix.webpack == 5 }}
SEND_NOTIFICATION: false
secrets:
CONSOLE_EMAIL: ${{ secrets.CONSOLE_EMAIL }}
Expand Down
91 changes: 91 additions & 0 deletions .github/workflows/e2e_scheduled_wp5.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: Flex Plugins CLI - E2E Nightly - WebPack5 (Node 22)

on:
schedule:
- cron: '15 4 * * *' # At 04:15 UTC (09:45 AM IST)
- cron: '15 5 * * *' # At 05:15 UTC (10:45 AM IST)
- cron: '15 6 * * *' # At 06:15 UTC (11:45 AM IST)
- cron: '15 7 * * *' # At 07:15 UTC (12:45 PM IST)
- cron: '15 8 * * *' # At 08:15 UTC (01:45 PM IST)

jobs:
scheduled-e2e-wp5-unix:
if: github.event.schedule == '15 4 * * *'
uses: ./.github/workflows/~reusable_e2e_by_OS.yaml
with:
OS: ubuntu-latest-large
BRANCH: main
NPM_IGNORE_PREFIX: ${{ vars.NPM_IGNORE_PREFIX }}
NODE_VERSION: '22'
WP5: true
PACKAGE_VERSION: latest
FLEX_UI_VERSION: latest
SLACK_TITLE: 'Flex Plugins CLI - E2E Nightly - WebPack5'
SEND_NOTIFICATION: true
secrets:
CONSOLE_EMAIL: ${{ secrets.CONSOLE_EMAIL }}
CONSOLE_PASSWORD: ${{ secrets.CONSOLE_PASSWORD }}
CONSOLE_EMAIL_linux: ${{ secrets.CONSOLE_EMAIL_linux }}
TWILIO_ACCOUNT_SID_linux: ${{ secrets.TWILIO_ACCOUNT_SID_linux }}
TWILIO_AUTH_TOKEN_linux: ${{ secrets.TWILIO_AUTH_TOKEN_linux }}
CONSOLE_EMAIL_win32: ${{ secrets.CONSOLE_EMAIL_win32 }}
TWILIO_ACCOUNT_SID_win32: ${{ secrets.TWILIO_ACCOUNT_SID_win32 }}
TWILIO_AUTH_TOKEN_win32: ${{ secrets.TWILIO_AUTH_TOKEN_win32 }}
CONSOLE_EMAIL_darwin: ${{ secrets.CONSOLE_EMAIL_darwin }}
TWILIO_ACCOUNT_SID_darwin: ${{ secrets.TWILIO_ACCOUNT_SID_darwin }}
TWILIO_AUTH_TOKEN_darwin: ${{ secrets.TWILIO_AUTH_TOKEN_darwin }}
SLACK_WEB_HOOK: ${{ secrets.SLACK_WEB_HOOK }}

scheduled-e2e-wp5-macos:
if: github.event.schedule == '15 6 * * *'
uses: ./.github/workflows/~reusable_e2e_by_OS.yaml
with:
OS: macos-latest
BRANCH: main
NPM_IGNORE_PREFIX: ${{ vars.NPM_IGNORE_PREFIX }}
NODE_VERSION: '22'
WP5: true
PACKAGE_VERSION: latest
FLEX_UI_VERSION: latest
SLACK_TITLE: 'Flex Plugins CLI - E2E Nightly - WebPack5'
SEND_NOTIFICATION: true
secrets:
CONSOLE_EMAIL: ${{ secrets.CONSOLE_EMAIL }}
CONSOLE_PASSWORD: ${{ secrets.CONSOLE_PASSWORD }}
CONSOLE_EMAIL_linux: ${{ secrets.CONSOLE_EMAIL_linux }}
TWILIO_ACCOUNT_SID_linux: ${{ secrets.TWILIO_ACCOUNT_SID_linux }}
TWILIO_AUTH_TOKEN_linux: ${{ secrets.TWILIO_AUTH_TOKEN_linux }}
CONSOLE_EMAIL_win32: ${{ secrets.CONSOLE_EMAIL_win32 }}
TWILIO_ACCOUNT_SID_win32: ${{ secrets.TWILIO_ACCOUNT_SID_win32 }}
TWILIO_AUTH_TOKEN_win32: ${{ secrets.TWILIO_AUTH_TOKEN_win32 }}
CONSOLE_EMAIL_darwin: ${{ secrets.CONSOLE_EMAIL_darwin }}
TWILIO_ACCOUNT_SID_darwin: ${{ secrets.TWILIO_ACCOUNT_SID_darwin }}
TWILIO_AUTH_TOKEN_darwin: ${{ secrets.TWILIO_AUTH_TOKEN_darwin }}
SLACK_WEB_HOOK: ${{ secrets.SLACK_WEB_HOOK }}

scheduled-e2e-wp5-windows:
if: github.event.schedule == '15 8 * * *'
uses: ./.github/workflows/~reusable_e2e_by_OS.yaml
with:
OS: windows-latest-large
BRANCH: main
NPM_IGNORE_PREFIX: ${{ vars.NPM_IGNORE_PREFIX }}
NODE_VERSION: '22'
WP5: true
PACKAGE_VERSION: latest
FLEX_UI_VERSION: latest
SLACK_TITLE: 'Flex Plugins CLI - E2E Nightly - WebPack5'
SEND_NOTIFICATION: true
secrets:
CONSOLE_EMAIL: ${{ secrets.CONSOLE_EMAIL }}
CONSOLE_PASSWORD: ${{ secrets.CONSOLE_PASSWORD }}
CONSOLE_EMAIL_linux: ${{ secrets.CONSOLE_EMAIL_linux }}
TWILIO_ACCOUNT_SID_linux: ${{ secrets.TWILIO_ACCOUNT_SID_linux }}
TWILIO_AUTH_TOKEN_linux: ${{ secrets.TWILIO_AUTH_TOKEN_linux }}
CONSOLE_EMAIL_win32: ${{ secrets.CONSOLE_EMAIL_win32 }}
TWILIO_ACCOUNT_SID_win32: ${{ secrets.TWILIO_ACCOUNT_SID_win32 }}
TWILIO_AUTH_TOKEN_win32: ${{ secrets.TWILIO_AUTH_TOKEN_win32 }}
CONSOLE_EMAIL_darwin: ${{ secrets.CONSOLE_EMAIL_darwin }}
TWILIO_ACCOUNT_SID_darwin: ${{ secrets.TWILIO_ACCOUNT_SID_darwin }}
TWILIO_AUTH_TOKEN_darwin: ${{ secrets.TWILIO_AUTH_TOKEN_darwin }}
SLACK_WEB_HOOK: ${{ secrets.SLACK_WEB_HOOK }}
51 changes: 51 additions & 0 deletions .github/workflows/lockfile_hygiene.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Lockfile Hygiene

# ADR 1634 Rule 3b — keep this public repo resolvable by external consumers.
# CI jobs redirect npm to Artifactory at build time (the artifactory-oidc
# action writes ~/.npmrc); the committed .npmrc / package-lock.json must
# always point at the public registry. Both jobs are secret-less and resolve
# from registry.npmjs.org only (no Artifactory, no OIDC).
#
# No paths-ignore: lockfile-only changes are exactly what this must check.

on:
pull_request:
branches:
- main
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read

jobs:
detect:
name: Detect Artifactory hosts
runs-on: ubuntu-x64
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Scan committed lockfiles & resolver config
run: bash .github/scripts/lockfile-hygiene.sh

clean-room-resolve:
name: Clean-room public resolve
runs-on: ubuntu-x64
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: '22'
- name: Strip any resolver config (simulate an external consumer)
run: |
rm -f ~/.npmrc || true
echo "registry=https://registry.npmjs.org/" > ~/.npmrc
echo "Effective npm registry:"
npm config get registry
- name: Clean-room install from the committed lockfile
env:
npm_config_registry: https://registry.npmjs.org/
run: |
npm ci --ignore-scripts --no-audit --fund=false
echo "✓ Lockfile resolves against the public registry."
Loading
Loading