Skip to content
Draft
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
71 changes: 71 additions & 0 deletions .github/workflows/nightly-publish-release-branches.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Nightly publish release branches

on:
schedule:
# Run nightly at 1 AM UTC
- cron: '0 1 * * *'
workflow_dispatch:

permissions:
contents: read
actions: write

jobs:
dispatch:
name: Dispatch publish workflow
runs-on: ubuntu-latest
steps:
- name: Dispatch publish-libs on release branches
uses: actions/github-script@v7
with:
script: |
const workflowId = 'publish-libs.yml'
const branches = ['release53', 'release52']

core.info(`Evaluating branches for nightly publish: ${branches.join(', ')}`)

for (const ref of branches) {
// Get current HEAD SHA for the branch
const branchInfo = await github.rest.repos.getBranch({
owner: context.repo.owner,
repo: context.repo.repo,
branch: ref,
})
const headSha = branchInfo.data?.commit?.sha
if (!headSha) {
core.warning(`Could not determine HEAD SHA for ${ref}, dispatching anyway`)
}

// Find the latest publish-libs run on that branch (workflow_dispatch)
const runs = await github.rest.actions.listWorkflowRuns({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: workflowId,
branch: ref,
event: 'workflow_dispatch',
per_page: 1,
})

const latest = runs.data?.workflow_runs?.[0]
const latestSha = latest?.head_sha
const latestConclusion = latest?.conclusion

if (headSha && latestSha === headSha && latestConclusion === 'success') {
core.info(`Skipping ${ref}: HEAD ${headSha.substring(0, 7)} already published successfully in last run ${latest.id}`)
continue
}

core.info(
`Dispatching ${ref}: HEAD=${headSha ? headSha.substring(0, 7) : 'unknown'} (last=${latestSha ? latestSha.substring(0, 7) : 'none'}, conclusion=${latestConclusion ?? 'none'})`
)

await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: workflowId,
ref,
inputs: {
nightly_mode: 'true',
},
})
}
153 changes: 143 additions & 10 deletions .github/workflows/publish-libs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ name: Publish libraries
on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
inputs:
nightly_mode:
description: 'Enable nightly change detection + skip publishing when no packages changed'
required: false
default: 'false'
type: choice
options:
- 'false'
- 'true'
push:
tags:
- "v**"
Expand All @@ -14,6 +23,7 @@ env:
IS_UPSTREAM: ${{ github.repository_owner == 'Sofie-Automation' }}
NPM_PACKAGE_SCOPE: ${{ vars.NPM_PACKAGE_SCOPE }} # In the form of nrkno, without the @
NPM_PACKAGE_PREFIX: ${{ vars.NPM_PACKAGE_PREFIX }} # Set to anything to prefix the published package names with "sofie-". eg in combination with NPM_PACKAGE_SCOPE this will turn @sofie-automation/shared-lib into @nrkno/sofie-shared-lib
IS_NIGHTLY: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.nightly_mode == 'true') }}

jobs:
check-publish:
Expand Down Expand Up @@ -138,6 +148,9 @@ jobs:

if: ${{ needs.check-publish.outputs.can-publish == '1' }}

outputs:
should_publish: ${{ steps.detect.outputs.should_publish }}

steps:
- uses: actions/checkout@v6
with:
Expand All @@ -154,8 +167,86 @@ jobs:
yarn install
env:
CI: true
- name: Bump version
if: ${{ github.event_name == 'workflow_dispatch' }}
- name: Build
run: |
cd packages
yarn build
env:
CI: true
- name: Decide whether to publish (scheduled builds may skip)
id: detect
run: |
cd packages

# Default: publish for non-nightly events
if [ "${{ env.IS_NIGHTLY }}" != "true" ]; then
echo "should_publish=1" >> $GITHUB_OUTPUT
exit 0
fi

# Check each package for changes
PACKAGES="blueprints-integration server-core-integration shared-lib live-status-gateway-api openapi"
HAS_ANY_CHANGES=0

echo "## Package Change Detection" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

for PKG in $PACKAGES; do
# Get current package name
if [ "${{ env.IS_UPSTREAM }}" = "true" ]; then
PACKAGE_NAME="@sofie-automation/$PKG"
else
PACKAGE_NAME="@${{ env.NPM_PACKAGE_SCOPE }}/${{ env.NPM_PACKAGE_PREFIX }}$PKG"
fi

# Get latest nightly version from npm
LATEST_NIGHTLY=$(npm view $PACKAGE_NAME dist-tags.nightly 2>/dev/null || echo "")

if [ -z "$LATEST_NIGHTLY" ]; then
echo "📦 **$PKG**: No nightly found, will publish" >> $GITHUB_STEP_SUMMARY
HAS_ANY_CHANGES=1
continue
fi

# Extract dist hash from version (format: X.Y.Z-nightly-branch-YYYYMMDD-HHMMSS-githash-disthash)
LAST_DIST_HASH=$(echo "$LATEST_NIGHTLY" | grep -oP '[0-9a-f]{8}$' || echo "")

if [ -z "$LAST_DIST_HASH" ]; then
echo "📦 **$PKG**: Old version format ($LATEST_NIGHTLY), will publish" >> $GITHUB_STEP_SUMMARY
HAS_ANY_CHANGES=1
continue
fi

# Compute current dist hash for this package
if [ -d "$PKG/dist" ]; then
CURRENT_DIST_HASH=$(find "$PKG/dist" -type f -print0 | sort -z | xargs -0 cat | sha256sum | cut -c1-8)
else
echo "❌ **$PKG**: No dist folder after build" >> $GITHUB_STEP_SUMMARY
echo "Build did not produce $PKG/dist; failing." >&2
exit 1
fi

if [ "$LAST_DIST_HASH" = "$CURRENT_DIST_HASH" ]; then
echo "✅ **$PKG**: No changes ($CURRENT_DIST_HASH)" >> $GITHUB_STEP_SUMMARY
else
echo "📦 **$PKG**: Changed ($LAST_DIST_HASH → $CURRENT_DIST_HASH)" >> $GITHUB_STEP_SUMMARY
HAS_ANY_CHANGES=1
fi
done

echo "" >> $GITHUB_STEP_SUMMARY
if [ $HAS_ANY_CHANGES -eq 0 ]; then
echo "**Result**: No packages changed, skipping publish" >> $GITHUB_STEP_SUMMARY
echo "should_publish=0" >> $GITHUB_OUTPUT
exit 0
fi

echo "**Result**: Will publish changed packages" >> $GITHUB_STEP_SUMMARY
echo "should_publish=1" >> $GITHUB_OUTPUT
env:
CI: true
- name: Bump version with per-package dist hashes
if: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') && steps.detect.outputs.should_publish == '1' }}
run: |
cd packages
COMMIT_TIMESTAMP=$(git log -1 --pretty=format:%ct HEAD)
Expand All @@ -166,30 +257,70 @@ jobs:
git config --global user.email "[email protected]"
git config --global user.name "superflytvab"

yarn set-version-and-commit prerelease --preid $PRERELEASE_TAG-$COMMIT_DATE-$GIT_HASH
env:
CI: true
- name: Build
run: |
cd packages
yarn build
# Update each package version with its own dist hash
for PKG_DIR in blueprints-integration server-core-integration shared-lib live-status-gateway-api openapi; do
if [ -d "$PKG_DIR/dist" ]; then
# Compute dist hash for this specific package
DIST_HASH=$(find "$PKG_DIR/dist" -type f -print0 | sort -z | xargs -0 cat | sha256sum | cut -c1-8)

# Get current version from package.json
CURRENT_VERSION=$(node -p "require('./$PKG_DIR/package.json').version")

# Compute new prerelease version
NEW_VERSION=$(node -e "
const semver = require('semver');
const current = '$CURRENT_VERSION';
const parsed = semver.parse(current);
const newVersion = \`\${parsed.major}.\${parsed.minor}.\${parsed.patch}-$PRERELEASE_TAG-$COMMIT_DATE-$GIT_HASH-$DIST_HASH\`;
console.log(newVersion);
")

# Update package.json
node -e "
const fs = require('fs');
const path = './$PKG_DIR/package.json';
const pkg = JSON.parse(fs.readFileSync(path, 'utf8'));
pkg.version = '$NEW_VERSION';
fs.writeFileSync(path, JSON.stringify(pkg, null, 2) + '\n');
"

echo "Updated $PKG_DIR to $NEW_VERSION"
else
echo "Missing $PKG_DIR/dist after build; failing." >&2
exit 1
fi
done

# Also update lerna.json with a representative version (use blueprints-integration)
if [ -f "blueprints-integration/package.json" ]; then
LERNA_VERSION=$(node -p "require('./blueprints-integration/package.json').version")
node -e "
const fs = require('fs');
const lerna = JSON.parse(fs.readFileSync('./lerna.json', 'utf8'));
lerna.version = '$LERNA_VERSION';
fs.writeFileSync('./lerna.json', JSON.stringify(lerna, null, 2) + '\n');
"
fi
env:
CI: true

- name: Build OpenAPI client library
if: ${{ env.IS_NIGHTLY != 'true' || steps.detect.outputs.should_publish == '1' }}
run: |
cd packages/openapi
yarn build
env:
CI: true
- name: Modify dependencies to use npm packages
if: ${{ env.IS_NIGHTLY != 'true' || steps.detect.outputs.should_publish == '1' }}
run: |
node scripts/prepublish.js "${{ github.repository }}" "${{ env.NPM_PACKAGE_SCOPE }}" ${{ env.NPM_PACKAGE_PREFIX }}

cd packages
yarn install --no-immutable

- name: Upload release artifact
if: ${{ env.IS_NIGHTLY != 'true' || steps.detect.outputs.should_publish == '1' }}
uses: actions/upload-artifact@v5
with:
name: publish-dist
Expand All @@ -210,6 +341,8 @@ jobs:
- prepare-publish
- test-packages

if: ${{ env.IS_NIGHTLY != 'true' || needs.prepare-publish.outputs.should_publish == '1' }}

permissions:
contents: write
id-token: write # scoped for as short as possible, as this gives write access to npm
Expand Down Expand Up @@ -256,7 +389,7 @@ jobs:
yarn install

NPM_TAG=nightly
if [ "${{ github.event_name }}" != "workflow_dispatch" ]; then
if [ "${{ github.event_name }}" != "workflow_dispatch" ] && [ "${{ github.event_name }}" != "schedule" ]; then
PACKAGE_NAME=$(node -p "require('./shared-lib/package.json').name")
PUBLISHED_VERSION=$(yarn npm info --json $PACKAGE_NAME | jq -c '.version' -r)
THIS_VERSION=$(node -p "require('./lerna.json').version")
Expand Down
Loading