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
370 changes: 370 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,370 @@
name: Release Automation

on:
workflow_dispatch:
inputs:
release_type:
description: 'Release type'
required: true
type: choice
options:
- minor
- patch
- rc
- final
base_minor:
description: 'Base minor version (e.g., "25.15") - required for minor/patch/rc/final'
required: false
type: string
force_version:
description: 'Force specific version (optional, overrides automatic planning)'
required: false
type: string
auto_approve_notes:
description: 'Auto-approve release notes without manual review'
required: false
type: boolean
default: false
dry_run:
description: 'Dry run - plan release without making changes'
required: false
type: boolean
default: false

env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

jobs:
plan:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.plan.outputs.version }}
tag: ${{ steps.plan.outputs.tag }}
branch: ${{ steps.plan.outputs.branch }}
needs_new_branch: ${{ steps.plan.outputs.needs_new_branch }}
is_prerelease: ${{ steps.plan.outputs.is_prerelease }}
release_type: ${{ steps.plan.outputs.release_type }}
previous_tag: ${{ steps.plan.outputs.previous_tag }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for tags
token: ${{ secrets.GITHUB_TOKEN }}

- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: latest
run_install: false

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'pnpm'

- name: Install dependencies
run: pnpm install

- name: Plan release
id: plan
run: |
set -e

echo "🎯 Planning release..."
echo "Release type: ${{ inputs.release_type }}"
echo "Base minor: ${{ inputs.base_minor }}"
echo "Force version: ${{ inputs.force_version }}"
echo "Dry run: ${{ inputs.dry_run }}"

# Run the planning script
PLAN_OUTPUT=$(node scripts/release/plan.js "${{ inputs.release_type }}" "${{ inputs.base_minor }}" "${{ inputs.force_version }}")
echo "Plan output: $PLAN_OUTPUT"

# Parse JSON output
VERSION=$(echo "$PLAN_OUTPUT" | jq -r '.version')
TAG=$(echo "$PLAN_OUTPUT" | jq -r '.tag')
BRANCH=$(echo "$PLAN_OUTPUT" | jq -r '.branch')
NEEDS_NEW_BRANCH=$(echo "$PLAN_OUTPUT" | jq -r '.needsNewBranch')
IS_PRERELEASE=$(echo "$PLAN_OUTPUT" | jq -r '.isPrerelease')
RELEASE_TYPE=$(echo "$PLAN_OUTPUT" | jq -r '.releaseType')

# Find previous tag for release notes
PREVIOUS_TAG=$(git tag -l --sort=-version:refname | head -1 || echo "")

echo "✅ Release planned:"
echo " Version: $VERSION"
echo " Tag: $TAG"
echo " Branch: $BRANCH"
echo " Needs new branch: $NEEDS_NEW_BRANCH"
echo " Is prerelease: $IS_PRERELEASE"
echo " Release type: $RELEASE_TYPE"
echo " Previous tag: $PREVIOUS_TAG"

# Set outputs
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "branch=$BRANCH" >> $GITHUB_OUTPUT
echo "needs_new_branch=$NEEDS_NEW_BRANCH" >> $GITHUB_OUTPUT
echo "is_prerelease=$IS_PRERELEASE" >> $GITHUB_OUTPUT
echo "release_type=$RELEASE_TYPE" >> $GITHUB_OUTPUT
echo "previous_tag=$PREVIOUS_TAG" >> $GITHUB_OUTPUT

- name: Validate plan
run: |
# Validate that we have all required information
if [ -z "${{ steps.plan.outputs.version }}" ]; then
echo "❌ Version planning failed"
exit 1
fi

# Check if tag already exists
if git tag -l | grep -q "^${{ steps.plan.outputs.tag }}$"; then
echo "❌ Tag ${{ steps.plan.outputs.tag }} already exists"
exit 1
fi

echo "✅ Plan validation passed"

generate-notes:
Comment on lines +39 to +130

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI about 1 month ago

To fix this problem, you should add a permissions key at the workflow root in .github/workflows/release.yml. The minimal starting point is usually contents: read, which allows jobs to check out code and read repository contents, which all jobs in this workflow require. If parts of the workflow need elevated permissions (for example, creating releases, uploading artifacts, creating or approving pull requests), those jobs can have their own permissions block specifying the minimal additional scopes they need (contents: write, pull-requests: write, etc.). As a starting fix, add the following at the top level (right below name:), giving all jobs only the ability to read repo contents via the GITHUB_TOKEN. Depending on further analysis, you may need to add per-job permission scopes later.

The fix requires:

  • Adding the following block after the name: line:
permissions:
  contents: read

No imports or other code changes are required.


Suggested changeset 1
.github/workflows/release.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,4 +1,6 @@
 name: Release Automation
+permissions:
+  contents: read
 
 on:
   workflow_dispatch:
EOF
@@ -1,4 +1,6 @@
name: Release Automation
permissions:
contents: read

on:
workflow_dispatch:
Copilot is powered by AI and may make mistakes. Always verify output.
runs-on: ubuntu-latest
needs: plan
if: ${{ !inputs.dry_run }}
outputs:
release_notes: ${{ steps.notes.outputs.release_notes }}
notes_file: ${{ steps.notes.outputs.notes_file }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: latest
run_install: false

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'pnpm'

- name: Install dependencies
run: pnpm install

- name: Generate release notes
id: notes
run: |
set -e

echo "📝 Generating release notes..."
echo "From tag: ${{ needs.plan.outputs.previous_tag }}"
echo "To ref: HEAD"
echo "Version: ${{ needs.plan.outputs.version }}"

# Generate release notes
if [ -n "${{ needs.plan.outputs.previous_tag }}" ]; then
node scripts/release/generate-notes.js \
"${{ needs.plan.outputs.previous_tag }}" \
"HEAD" \
"${{ needs.plan.outputs.version }}"
else
echo "# Release ${{ needs.plan.outputs.version }}" > /tmp/release-notes-${{ needs.plan.outputs.version }}.md
echo "" >> /tmp/release-notes-${{ needs.plan.outputs.version }}.md
echo "Initial release." >> /tmp/release-notes-${{ needs.plan.outputs.version }}.md
fi

NOTES_FILE="/tmp/release-notes-${{ needs.plan.outputs.version }}.md"

echo "✅ Release notes generated"
echo "Notes file: $NOTES_FILE"

# Set outputs
echo "notes_file=$NOTES_FILE" >> $GITHUB_OUTPUT

# Read the file content for output (escape newlines)
RELEASE_NOTES=$(cat "$NOTES_FILE" | sed ':a;N;$!ba;s/\n/\\n/g')
echo "release_notes=$RELEASE_NOTES" >> $GITHUB_OUTPUT

- name: Upload release notes
uses: actions/upload-artifact@v4
with:
name: release-notes-${{ needs.plan.outputs.version }}
path: /tmp/release-notes-${{ needs.plan.outputs.version }}.md
retention-days: 30

approve-notes:
Comment on lines +131 to +200

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI about 1 month ago

To mitigate the risk, you should add an explicit permissions block with the least-privilege necessary either at the workflow level (top-level, applying to all jobs by default) or specifically on jobs that need special permissions. As a minimal baseline, set permissions: { contents: read } at the top of the workflow (below name: and above on:), unless some jobs need more. Then, if any job needs broader or different permissions, override at the job level.

For this workflow, at a minimum, add the following at the top:

permissions:
  contents: read

If any job requires more (e.g., release creation, which might need contents: write or packages: write), that job should have its own explicit block.

In this edit, only the code provided is to be changed, so add the block after the name: Release Automation (line 1).

Suggested changeset 1
.github/workflows/release.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,4 +1,6 @@
 name: Release Automation
+permissions:
+  contents: read
 
 on:
   workflow_dispatch:
EOF
@@ -1,4 +1,6 @@
name: Release Automation
permissions:
contents: read

on:
workflow_dispatch:
Copilot is powered by AI and may make mistakes. Always verify output.
runs-on: ubuntu-latest
needs: [plan, generate-notes]
if: ${{ !inputs.dry_run && !inputs.auto_approve_notes }}
environment: release-approval
steps:
- name: Display release notes for approval
run: |
echo "📋 Please review the release notes for version ${{ needs.plan.outputs.version }}:"
echo ""
echo "${{ needs.generate-notes.outputs.release_notes }}" | sed 's/\\n/\n/g'
echo ""
echo "🔍 Release Plan Summary:"
echo " Version: ${{ needs.plan.outputs.version }}"
echo " Tag: ${{ needs.plan.outputs.tag }}"
echo " Branch: ${{ needs.plan.outputs.branch }}"
echo " Release Type: ${{ needs.plan.outputs.release_type }}"
echo " Is Prerelease: ${{ needs.plan.outputs.is_prerelease }}"
echo ""
echo "✅ Approve this environment to proceed with the release"

release:
Comment on lines +201 to +221

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {}

Copilot Autofix

AI about 1 month ago

To resolve the CodeQL finding and adhere to security best practices:

  • Explicitly set a permissions key for the approve-notes job in .github/workflows/release.yml.
  • This job only displays release notes and does not require write access; setting permissions: read-all is appropriate and minimal.
  • The change involves inserting a permissions: read-all block in the approve-notes job (directly after its job header at line 201, before or after runs-on but typically immediately after the job name).

No additional imports, methods, or variable definitions are needed. Only the workflow YAML is changed.


Suggested changeset 1
.github/workflows/release.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -198,6 +198,7 @@
           retention-days: 30
 
   approve-notes:
+    permissions: read-all
     runs-on: ubuntu-latest
     needs: [plan, generate-notes]
     if: ${{ !inputs.dry_run && !inputs.auto_approve_notes }}
EOF
@@ -198,6 +198,7 @@
retention-days: 30

approve-notes:
permissions: read-all
runs-on: ubuntu-latest
needs: [plan, generate-notes]
if: ${{ !inputs.dry_run && !inputs.auto_approve_notes }}
Copilot is powered by AI and may make mistakes. Always verify output.
runs-on: ubuntu-latest
needs: [plan, generate-notes, approve-notes]
if: ${{ !inputs.dry_run && (inputs.auto_approve_notes || success()) }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

- name: Create release branch
if: ${{ needs.plan.outputs.needs_new_branch == 'true' }}
run: |
echo "🌿 Creating new branch: ${{ needs.plan.outputs.branch }}"
git checkout -b "${{ needs.plan.outputs.branch }}"
git push origin "${{ needs.plan.outputs.branch }}"

- name: Checkout release branch
if: ${{ needs.plan.outputs.needs_new_branch == 'false' }}
run: |
echo "🔄 Switching to existing branch: ${{ needs.plan.outputs.branch }}"
git fetch origin "${{ needs.plan.outputs.branch }}" || true
git checkout "${{ needs.plan.outputs.branch }}" || git checkout -b "${{ needs.plan.outputs.branch }}"

- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: latest
run_install: false

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'pnpm'

- name: Update version
run: |
echo "📝 Updating version to ${{ needs.plan.outputs.version }}"

# Update package.json using npm (works with pnpm too)
npm version "${{ needs.plan.outputs.version }}" --no-git-tag-version

# Run make versiontag to update other files
make versiontag

# Commit changes
git add .
git commit -m "chore: bump version to ${{ needs.plan.outputs.version }}" || echo "No changes to commit"

- name: Create and push tag
run: |
echo "🏷️ Creating tag: ${{ needs.plan.outputs.tag }}"
git tag -a "${{ needs.plan.outputs.tag }}" -m "Release ${{ needs.plan.outputs.version }}"
git push origin "${{ needs.plan.outputs.tag }}"

# Push branch changes
git push origin "${{ needs.plan.outputs.branch }}"

- name: Download release notes
uses: actions/download-artifact@v4
with:
name: release-notes-${{ needs.plan.outputs.version }}
path: ./

- name: Create GitHub release
run: |
echo "🚀 Creating GitHub release..."

NOTES_FILE="release-notes-${{ needs.plan.outputs.version }}.md"

gh release create "${{ needs.plan.outputs.tag }}" \
--title "Release ${{ needs.plan.outputs.version }}" \
--notes-file "$NOTES_FILE" \
${{ needs.plan.outputs.is_prerelease == 'true' && '--prerelease' || '' }} \
--target "${{ needs.plan.outputs.branch }}"

echo "✅ Release created successfully"

- name: Trigger package build
if: ${{ needs.plan.outputs.is_prerelease == 'false' }}
run: |
echo "📦 Triggering package build workflow..."
# The package.yml workflow will be triggered by the release event

notify:
Comment on lines +222 to +312

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI about 1 month ago

To fix this, specify a permissions: block for the workflow or (preferably, for fine-grained control) at the job level. For the release job, the following minimum permissions are usually needed:

  • contents: write: To push new branches/tags and create GitHub releases.
  • pull-requests: read: Not strictly needed here.
  • packages: write and similar are not needed unless packages are published explicitly.

For the rest of the jobs, if they do not interact with the repository contents (e.g., if they only read artifacts or run CI checks), use contents: read as the minimum.

The best approach is:

  • Add a root-level permissions: block with contents: read (applies to all jobs by default).
  • Override in the release job to set contents: write.

Steps:

  1. Add at the top of the file (after the name: and before on:) a block:
    permissions:
      contents: read
  2. Within the release: job (after runs-on: ubuntu-latest), add:
      permissions:
        contents: write

This ensures all jobs have least privilege, and only the release job can write to repository contents.


Suggested changeset 1
.github/workflows/release.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,5 +1,8 @@
 name: Release Automation
 
+permissions:
+  contents: read
+
 on:
   workflow_dispatch:
     inputs:
@@ -220,6 +223,8 @@
 
   release:
     runs-on: ubuntu-latest
+    permissions:
+      contents: write
     needs: [plan, generate-notes, approve-notes]
     if: ${{ !inputs.dry_run && (inputs.auto_approve_notes || success()) }}
     steps:
EOF
@@ -1,5 +1,8 @@
name: Release Automation

permissions:
contents: read

on:
workflow_dispatch:
inputs:
@@ -220,6 +223,8 @@

release:
runs-on: ubuntu-latest
permissions:
contents: write
needs: [plan, generate-notes, approve-notes]
if: ${{ !inputs.dry_run && (inputs.auto_approve_notes || success()) }}
steps:
Copilot is powered by AI and may make mistakes. Always verify output.
runs-on: ubuntu-latest
needs: [plan, release]
if: ${{ !inputs.dry_run && always() }}
steps:
- name: Checkout repository
uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: latest
run_install: false

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'pnpm'

- name: Install dependencies
run: pnpm install

- name: Send Teams notification
if: ${{ vars.TEAMS_WEBHOOK_URL }}
run: |
if [ "${{ needs.release.result }}" = "success" ]; then
echo "✅ Release ${{ needs.plan.outputs.version }} completed successfully!"
RELEASE_URL="https://github.com/lablup/backend.ai-webui/releases/tag/${{ needs.plan.outputs.tag }}"

node scripts/release/teams-notify.js \
"${{ vars.TEAMS_WEBHOOK_URL }}" \
"${{ needs.plan.outputs.version }}" \
"${{ needs.plan.outputs.tag }}" \
"$RELEASE_URL" \
"${{ needs.plan.outputs.is_prerelease }}" \
"true"
else
echo "❌ Release ${{ needs.plan.outputs.version }} failed!"

node scripts/release/teams-notify.js \
"${{ vars.TEAMS_WEBHOOK_URL }}" \
"${{ needs.plan.outputs.version }}" \
"${{ needs.plan.outputs.tag }}" \
"" \
"${{ needs.plan.outputs.is_prerelease }}" \
"false" \
"Release workflow failed. Check GitHub Actions for details."
fi

- name: Log completion
run: |
if [ "${{ needs.release.result }}" = "success" ]; then
echo "✅ Release ${{ needs.plan.outputs.version }} completed successfully!"
echo "🔗 Release URL: https://github.com/lablup/backend.ai-webui/releases/tag/${{ needs.plan.outputs.tag }}"
else
echo "❌ Release ${{ needs.plan.outputs.version }} failed!"
echo "📋 Check the workflow logs for details"
fi
Comment on lines +313 to +370

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI about 1 month ago

To fix the problem, we should explicitly specify the permissions key at the root of the workflow file, so all jobs inherit these permissions unless overridden. The most secure approach is to grant only the minimum required permissions. As a baseline, setting contents: read is always safe; individual jobs can be given more rights as needed. Review of the workflow suggests that most jobs will only require read-level access, but jobs that create releases, push tags, or otherwise need to modify repo contents should be granted the required write permissions at the job level (not globally).

For the quickest fix for the CodeQL warning, you should add at the top-level (after the name: declaration and before on:):

permissions:
  contents: read

This sets minimal read permissions by default. On jobs that require more (e.g., the release job, for creating releases and pushing tags), you should override and specify the minimum write permissions needed. For example, in the release job, set:

permissions:
  contents: write

Other jobs (such as plan, generate-notes, approve-notes, notify) can inherit the contents: read default unless their steps require more access.

Summary:

  • Add a permissions: contents: read block at the top-level (after name:).
  • For the release job, add a permissions: contents: write block.
  • No new imports, methods, or definitions are needed; only YAML blocks are added.

Suggested changeset 1
.github/workflows/release.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,5 +1,8 @@
 name: Release Automation
 
+permissions:
+  contents: read
+
 on:
   workflow_dispatch:
     inputs:
@@ -222,6 +225,8 @@
     runs-on: ubuntu-latest
     needs: [plan, generate-notes, approve-notes]
     if: ${{ !inputs.dry_run && (inputs.auto_approve_notes || success()) }}
+    permissions:
+      contents: write
     steps:
       - name: Checkout repository
         uses: actions/checkout@v4
EOF
@@ -1,5 +1,8 @@
name: Release Automation

permissions:
contents: read

on:
workflow_dispatch:
inputs:
@@ -222,6 +225,8 @@
runs-on: ubuntu-latest
needs: [plan, generate-notes, approve-notes]
if: ${{ !inputs.dry_run && (inputs.auto_approve_notes || success()) }}
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
Copilot is powered by AI and may make mistakes. Always verify output.
Loading