Skip to content

Update Automation

Update Automation #97

name: Update Automation
on:
# schedule:
# - cron: '0 0 * * *'
workflow_dispatch:
jobs:
update-automation:
name: Run Automation Tasks
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
staging-branch: ${{ env.STAGING_BRANCH }}
existing-code-oss-tag: ${{ env.EXISTING_CODE_OSS_TAG }}
latest-code-oss-tag: ${{ env.LATEST_CODE_OSS_TAG }}
steps:
- name: Setup environment
run: |
echo "Installing required dependencies"
sudo apt-get update
sudo apt-get install -y quilt libxml2-utils jq
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Get latest semver branch
run: |
git fetch origin
LATEST_SEMVER=$(git branch -r | grep -E 'origin/[0-9]+\.[0-9]+$' | sed 's/.*origin\///' | sort -V | tail -1)
# For testing purposes
if [ -z "$LATEST_SEMVER" ]; then
LATEST_SEMVER="build-test"
echo "No semver branches found, using main"
fi
echo "LATEST_SEMVER=$LATEST_SEMVER" >> $GITHUB_ENV
echo "Using branch: $LATEST_SEMVER"
git checkout "$LATEST_SEMVER"
git submodule update --init --recursive
- name: Check if update needed
env:
GH_TOKEN: ${{ github.token }}
run: |
cd third-party-src
git fetch --tags
EXISTING_CODE_OSS_TAG=$(git describe --tags --exact-match HEAD 2>/dev/null | head -1)
if [ -z "$EXISTING_CODE_OSS_TAG" ]; then
echo "Error: Submodule is not on a tagged commit"
exit 1
fi
cd ..
LATEST_CODE_OSS_TAG=$(gh api repos/microsoft/vscode/releases/latest --template '{{.tag_name}}')
echo "EXISTING_CODE_OSS_TAG=$EXISTING_CODE_OSS_TAG" >> $GITHUB_ENV
echo "LATEST_CODE_OSS_TAG=$LATEST_CODE_OSS_TAG" >> $GITHUB_ENV
echo "Existing tag: $EXISTING_CODE_OSS_TAG"
echo "Latest tag: $LATEST_CODE_OSS_TAG"
if [ "$EXISTING_CODE_OSS_TAG" = "$LATEST_CODE_OSS_TAG" ]; then
echo "Submodule is up to date with latest VS Code release"
exit 0
else
echo "Update needed: $EXISTING_CODE_OSS_TAG -> $LATEST_CODE_OSS_TAG"
# Create or checkout staging branch
STAGING_BRANCH="staging-code-editor-$LATEST_CODE_OSS_TAG"
if git show-ref --verify --quiet refs/remotes/origin/"$STAGING_BRANCH"; then
echo "Staging branch $STAGING_BRANCH already exists, checking it out"
git checkout -b "$STAGING_BRANCH" origin/"$STAGING_BRANCH"
else
echo "Creating new staging branch: $STAGING_BRANCH"
git checkout -b "$STAGING_BRANCH"
fi
# Update submodule to latest VS Code release
echo "Updating submodule to $LATEST_CODE_OSS_TAG"
cd third-party-src
git fetch --tags
git checkout "$EXISTING_CODE_OSS_TAG"
cd ..
git add third-party-src
if git diff --staged --quiet; then
echo "No changes to commit, submodule already up to date"
git push origin "$STAGING_BRANCH"
else
git commit -m "Update VS Code submodule to $LATEST_CODE_OSS_TAG"
git push origin "$STAGING_BRANCH"
fi
echo "STAGING_BRANCH=$STAGING_BRANCH" >> $GITHUB_ENV
echo "Created staging branch: $STAGING_BRANCH with VS Code $LATEST_CODE_OSS_TAG"
fi
- name: Rebase patches for all targets
run: |
# echo "Rebasing patches for all build targets"
# # Test each target sequentially with rebasing
# FAILED_TARGETS=()
# for target in code-editor-server code-editor-sagemaker-server code-editor-web-embedded code-editor-web-embedded-with-terminal; do
# echo ""
# echo "=== REBASING TARGET: $target ==="
# if ./scripts/prepare-src.sh --command rebase_patches "$target"; then
# echo "Successfully rebased $target"
# else
# echo "Failed to rebase $target"
# FAILED_TARGETS+=("$target")
# fi
# # Clean up for next target
# rm -rf code-editor-src
# echo "=== END TARGET: $target ==="
# done
# # Report results
# if [ ${#FAILED_TARGETS[@]} -gt 0 ]; then
# echo "Failed targets: ${FAILED_TARGETS[*]}"
# exit 1
# else
# echo "All targets rebased successfully"
# fi
# # Commit rebased patches if any changes
# git add patches/
# if ! git diff --staged --quiet; then
# git commit -m "Rebase patches for all targets"
# git push origin "$STAGING_BRANCH"
# fi
build-and-update-package-locks:
name: Build and Update Package Lock Overrides
runs-on: ubuntu-latest
needs: update-automation
if: needs.update-automation.outputs.staging-branch != ''
permissions:
contents: write
strategy:
matrix:
target: [code-editor-server, code-editor-sagemaker-server, code-editor-web-embedded, code-editor-web-embedded-with-terminal]
steps:
- name: Setup environment
run: |
sudo apt-get update
sudo apt-get install -y quilt libxml2-utils jq libx11-dev libxkbfile-dev
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ needs.update-automation.outputs.staging-branch }}
fetch-depth: 0
submodules: true
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Build target
run: |
./scripts/prepare-src.sh "${{ matrix.target }}"
cd code-editor-src
npm install
cd ..
# ./scripts/build-artifacts.sh ${{ matrix.target }}
echo "Built target: ${{ matrix.target }}"
- name: Update package-lock for target
run: |
OVERRIDE_PATH=$(jq -r '."package-lock-overrides".path' "configuration/${{ matrix.target }}.json")
rm -rf "$OVERRIDE_PATH"
mkdir -p "$OVERRIDE_PATH"
find code-editor-src -name "package-lock.json" -type f | while read -r file; do
rel_path="${file#code-editor-src/}"
third_party_file="third-party-src/$rel_path"
if [ ! -f "$third_party_file" ] || ! cmp -s "$file" "$third_party_file"; then
dest_dir="$OVERRIDE_PATH/$(dirname "$rel_path")"
mkdir -p "$dest_dir"
cp "$file" "$dest_dir/"
echo "Copied updated $rel_path to $OVERRIDE_PATH"
fi
done
- name: Upload prepared source as artifact
uses: actions/upload-artifact@v4
with:
name: ${{ github.run_id }}-prepared-source-${{ matrix.target }}
path: code-editor-src/
retention-days: 1
- name: Commit package-lock overrides
run: |
git add package-lock-overrides/
if ! git diff --staged --quiet; then
git commit -m "Update package-lock.json overrides for ${{ matrix.target }}"
# Retry push with rebase until successful
for i in {1..5}; do
if git pull --rebase origin "${{ needs.update-automation.outputs.staging-branch }}" && git push origin "${{ needs.update-automation.outputs.staging-branch }}"; then
break
fi
sleep $((i * 2))
done
fi
generate-oss-attribution:
name: Generate OSS Attribution
runs-on: ubuntu-latest
needs: [update-automation, build-and-update-package-locks]
if: needs.update-automation.outputs.staging-branch != ''
permissions:
contents: write
steps:
- name: Setup environment
run: |
sudo apt-get update
sudo apt-get install -y jq
npm i -g license-checker
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ needs.update-automation.outputs.staging-branch }}
fetch-depth: 0
submodules: true
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Download prepared sources from artifacts
uses: actions/download-artifact@v4
with:
pattern: ${{ github.run_id }}-prepared-source-*
merge-multiple: false
- name: Organize downloaded sources
run: |
for target in code-editor-server code-editor-sagemaker-server code-editor-web-embedded code-editor-web-embedded-with-terminal; do
if [[ -d "${{ github.run_id }}-prepared-source-$target" ]]; then
mv "${{ github.run_id }}-prepared-source-$target" "code-editor-src-$target"
echo "Organized prepared source for $target"
else
echo "Missing prepared source artifact for target: $target"
exit 1
fi
done
- name: Generate unified OSS attribution
run: |
./scripts/generate-oss-attribution.sh --command generate_unified_oss_attribution
- name: Commit OSS attribution
run: |
# Copy LICENSE-THIRD-PARTY to root directory
cp overrides/LICENSE-THIRD-PARTY LICENSE-THIRD-PARTY
git add overrides/ LICENSE-THIRD-PARTY
if ! git diff --staged --quiet; then
git commit -m "Update unified OSS attribution"
git push origin "${{ needs.update-automation.outputs.staging-branch }}"
fi
- name: Clean up prepared source directories
run: |
rm -rf code-editor-src-*
echo "Cleaned up local prepared source directories"
create-pr:
name: Create Pull Request
runs-on: ubuntu-latest
needs: [update-automation, build-and-update-package-locks, generate-oss-attribution]
if: needs.update-automation.outputs.staging-branch != ''
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ needs.update-automation.outputs.staging-branch }}
fetch-depth: 0
- name: Determine target branch and create PR
env:
GH_TOKEN: ${{ github.token }}
run: |
# Check if PR already exists for this staging branch
EXISTING_PR=$(gh pr list --head "${{ needs.update-automation.outputs.staging-branch }}" --json number --jq '.[0].number' || echo "")
if [ -n "$EXISTING_PR" ]; then
echo "PR already exists for branch ${{ needs.update-automation.outputs.staging-branch }}: #$EXISTING_PR"
exit 0
fi
EXISTING_CODE_OSS_TAG="${{ needs.update-automation.outputs.initial-tag }}"
LATEST_CODE_OSS_TAG="${{ needs.update-automation.outputs.current-tag }}"
# Parse version numbers
EXISTING_CODE_OSS_MAJOR=$(echo "$EXISTING_CODE_OSS_TAG" | cut -d. -f1)
EXISTING_CODE_OSS_MINOR=$(echo "$EXISTING_CODE_OSS_TAG" | cut -d. -f2)
LATEST_CODE_OSS_MAJOR=$(echo "$LATEST_CODE_OSS_TAG" | cut -d. -f1)
LATEST_CODE_OSS_MINOR=$(echo "$LATEST_CODE_OSS_TAG" | cut -d. -f2)
git fetch origin
LATEST_SEMVER=$(git branch -r | grep -E 'origin/[0-9]+\.[0-9]+$' | sed 's/.*origin\///' | sort -V | tail -1)
if [ -n "$LATEST_SEMVER" ]; then
LATEST_MAJOR=$(echo "$LATEST_SEMVER" | cut -d. -f1)
LATEST_MINOR=$(echo "$LATEST_SEMVER" | cut -d. -f2)
else
LATEST_MAJOR=0
LATEST_MINOR=0
fi
# Determine if this is a patch or minor/major update
if [ "$LATEST_CODE_OSS_MAJOR" = "$EXISTING_CODE_OSS_MAJOR" ] && [ "$LATEST_CODE_OSS_MINOR" = "$EXISTING_CODE_OSS_MINOR" ]; then
# Patch update - use existing branch
TARGET_BRANCH="$LATEST_SEMVER"
echo "Patch update detected: targeting existing branch $TARGET_BRANCH"
else
# Minor/major update - create new branch
LATEST_MAJOR=$(echo "$LATEST_SEMVER" | cut -d. -f1)
LATEST_MINOR=$(echo "$LATEST_SEMVER" | cut -d. -f2)
NEW_MINOR=$((LATEST_MINOR + 1))
TARGET_BRANCH="$LATEST_MAJOR.$NEW_MINOR"
echo "Minor/major update detected: targeting new branch $TARGET_BRANCH"
# Create new version branch if it doesn't exist
if ! git show-ref --verify --quiet refs/remotes/origin/"$TARGET_BRANCH"; then
echo "Creating new version branch: $TARGET_BRANCH"
git checkout -b "$TARGET_BRANCH" "origin/$LATEST_SEMVER"
git push origin "$TARGET_BRANCH"
fi
fi
# Get release body and generate links
RELEASE_BODY=$(gh api repos/microsoft/vscode/releases/tags/$LATEST_CODE_OSS_TAG --template '{{.body}}')
VERSION_NUMBER=$(echo "$LATEST_CODE_OSS_TAG" | cut -d. -f1,2 | sed 's/\./\_/g')
VSCODE_RELEASE_NOTES="https://code.visualstudio.com/updates/v$VERSION_NUMBER"
# Create PR
PR_TITLE="Update VS Code to $LATEST_CODE_OSS_TAG"
PR_BODY="## Automated update of VS Code submodule to version $LATEST_CODE_OSS_TAG
Changes:
- Updated third-party-src submodule to $LATEST_CODE_OSS_TAG
- Rebased patches for all targets
- Updated package-lock.json overrides
- Updated OSS attribution
## Release Notes
[VS Code Release Notes]($VSCODE_RELEASE_NOTES)
### Code-OSS Release Details
$RELEASE_BODY
## Review Notes
Please review the release notes above for:
- Breaking changes
- Changes in default behavior
- Newly introduced features that may need to be disabled/modified
- Remote extension host changes
## Next Steps
After this PR is merged, a release can be created by following the guide in [RELEASE.md](RELEASE.md)."
gh pr create \
--title "$PR_TITLE" \
--body "$PR_BODY" \
--base "$TARGET_BRANCH" \
--head "${{ needs.update-automation.outputs.staging-branch }}"
echo "Created PR from ${{ needs.update-automation.outputs.staging-branch }} to $TARGET_BRANCH"
handle-failures:
name: Handle Failures
runs-on: ubuntu-latest
needs: [update-automation, build-and-update-package-locks, generate-oss-attribution, create-pr]
if: failure()
steps:
- name: Report rebase failures
run: |
# TODO: Implement metric reporting to CW.
exit 1