diff --git a/.github/workflows/publish-step-1.yml b/.github/workflows/publish-step-1.yml index 3de570eb..12ccc205 100644 --- a/.github/workflows/publish-step-1.yml +++ b/.github/workflows/publish-step-1.yml @@ -146,3 +146,322 @@ jobs: outputs: upload_url: ${{ steps.release_vars.outputs.upload_url }} + +name: Publish Step 1 + +on: + workflow_dispatch: + +jobs: + create_release: + runs-on: ubuntu-latest + + steps: + - name: Checkout main branch + uses: actions/checkout@v4 + with: + ref: main + fetch-depth: 0 # Fetch all history for all tags + + - name: Install Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Prepare shell envs + shell: bash + run: | + VERSION=$(cat VERSION) + echo "VERSION=${VERSION}" >> $GITHUB_ENV + TAG="v${VERSION}" + echo "TAG=${TAG}" >> $GITHUB_ENV + echo "PYTHON=python3" >> $GITHUB_ENV + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + - name: Tagging + shell: bash + env: + TAG: ${{ env.TAG }} + run: | + # Ensure latest commit is tagged with '$TAG' + NEW_COMMIT=$(git rev-parse HEAD) + if git rev-parse "$TAG" >/dev/null 2>&1; then + EXISTING_COMMIT=$(git rev-parse "$TAG") + if [ "$NEW_COMMIT" != "$EXISTING_COMMIT" ]; then + echo "Deleting existing tag $TAG" + git tag -d "$TAG" + git push --delete origin "$TAG" + else + echo "Tag $TAG already exists and points to the same commit. No changes needed." + exit 0 + fi + fi + echo "Creating new tag $TAG" + git tag "$TAG" + git push origin "$TAG" + + - name: Ensure draft release exists and set upload_url + id: release_vars + uses: actions/github-script@v7 + env: + TAG: ${{ env.TAG }} + with: + script: | + const fs = require('fs'); + const path = require('path'); + const tag_name = process.env.TAG; + console.log(`tag_name: ${tag_name}`); + let upload_url; + + try { + // Check if the release already exists + const { data: releases } = await github.rest.repos.listReleases({ + owner: context.repo.owner, + repo: context.repo.repo, + }); + let release = releases.find(release => release.tag_name === tag_name); + + if (release) { + console.log(`Existing release found: ${JSON.stringify(release, null, 2)}`); + if (!release.draft) { + console.log(`Release for tag ${tag_name} is already published. Exiting with error.`); + core.setFailed(`Release for tag ${tag_name} is already published.`); + return; + } + + // Update the release to point to potentially new commit (noop when no change) + const release_id = release.id; + await github.rest.repos.updateRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: release_id, + tag_name: tag_name, + target_commitish: context.sha, + }); + + // Retrieve the latest draft release object to get the upload_url + const { data: updatedRelease } = await github.rest.repos.getRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: release_id, + }); + + console.log(`getRelease result: ${JSON.stringify(updatedRelease, null, 2)}`); + + upload_url = updatedRelease.upload_url; + } else { + // Read release notes from file + const release_notes_path = path.join(process.env.GITHUB_WORKSPACE, 'temp', 'DRAFT_RELEASE_NOTES.md'); + const release_notes = fs.readFileSync(release_notes_path, 'utf8'); + + // Release does not exist, so create it. + const response = await github.rest.repos.createRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + tag_name: tag_name, + name: tag_name, + body: release_notes, + draft: true, + prerelease: false, + }); + if (response.status >= 200 && response.status < 300 && response.data.upload_url) { + console.log(`Draft release created with tag [${tag_name}]`); + upload_url = response.data.upload_url; + } else { + core.setFailed(`Failed to create release with tag [${tag_name}] Status: ${response.status}`); + return; + } + } + } catch (error) { + core.setFailed(`Error while creating draft release: ${error.message}`); + return; + } + + console.log(`upload_url: ${upload_url}`); + core.setOutput("upload_url", upload_url); + +name: Publish Step 1 + +on: + workflow_dispatch: + +jobs: + create_release: + runs-on: ubuntu-latest + + steps: + - name: Checkout main branch + uses: actions/checkout@v4 + with: + ref: main + fetch-depth: 0 # Fetch all history for all tags + + - name: Install Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Prepare shell envs + shell: bash + run: | + VERSION=$(cat VERSION) + echo "VERSION=${VERSION}" >> $GITHUB_ENV + TAG="v${VERSION}" + echo "TAG=${TAG}" >> $GITHUB_ENV + echo "PYTHON=python3" >> $GITHUB_ENV + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + - name: Tagging + shell: bash + env: + TAG: ${{ env.TAG }} + run: | + # Ensure latest commit is tagged with '$TAG' + NEW_COMMIT=$(git rev-parse HEAD) + if git rev-parse "$TAG" >/dev/null 2>&1; then + EXISTING_COMMIT=$(git rev-parse "$TAG") + if [ "$NEW_COMMIT" != "$EXISTING_COMMIT" ]; then + echo "Deleting existing tag $TAG" + git tag -d "$TAG" + git push --delete origin "$TAG" + else + echo "Tag $TAG already exists and points to the same commit. No changes needed." + exit 0 + fi + fi + echo "Creating new tag $TAG" + git tag "$TAG" + git push origin "$TAG" + + - name: Ensure draft release exists and set upload_url + id: release_vars + uses: actions/github-script@v7 + env: + TAG: ${{ env.TAG }} + with: + script: | + const fs = require('fs'); + const path = require('path'); + const tag_name = process.env.TAG; + console.log(`tag_name: ${tag_name}`); + let upload_url; + + try { + // Check if the release already exists + const { data: releases } = await github.rest.repos.listReleases({ + owner: context.repo.owner, + repo: context.repo.repo, + }); + let release = releases.find(release => release.tag_name === tag_name); + + if (release) { + console.log(`Existing release found: ${JSON.stringify(release, null, 2)}`); + if (!release.draft) { + console.log(`Release for tag ${tag_name} is already published. Exiting with error.`); + core.setFailed(`Release for tag ${tag_name} is already published.`); + return; + } + + // Update the release to point to potentially new commit (noop when no change) + const release_id = release.id; + await github.rest.repos.updateRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: release_id, + tag_name: tag_name, + target_commitish: context.sha, + }); + + // Retrieve the latest draft release object to get the upload_url + const { data: updatedRelease } = await github.rest.repos.getRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: release_id, + }); + + console.log(`getRelease result: ${JSON.stringify(updatedRelease, null, 2)}`); + + upload_url = updatedRelease.upload_url; + } else { + // Read release notes from file + const release_notes_path = path.join(process.env.GITHUB_WORKSPACE, 'temp', 'DRAFT_RELEASE_NOTES.md'); + const release_notes = fs.readFileSync(release_notes_path, 'utf8'); + + // Release does not exist, so create it. + const response = await github.rest.repos.createRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + tag_name: tag_name, + name: tag_name, + body: release_notes, + draft: true, + prerelease: false, + }); + if (response.status >= 200 && response.status < 300 && response.data.upload_url) { + console.log(`Draft release created with tag [${tag_name}]`); + upload_url = response.data.upload_url; + } else { + core.setFailed(`Failed to create release with tag [${tag_name}] Status: ${response.status}`); + return; + } + } + } catch (error) { + core.setFailed(`Error while creating draft release: ${error.message}`); + return; + } + + console.log(`upload_url: ${upload_url}`); + core.setOutput("upload_url", upload_url); + + - name: Attach assets + uses: actions/github-script@v7 + env: + UPLOAD_URL: ${{ steps.release_vars.outputs.upload_url }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + script: | + const fs = require('fs'); + const path = require('path'); + const upload_url = process.env.UPLOAD_URL; + const dist_dir = path.join(process.env.GITHUB_WORKSPACE, 'dist'); + + const files = fs.readdirSync(dist_dir); + for (const file of files) { + const filePath = path.join(dist_dir, file); + const fileStat = fs.statSync(filePath); + + try { + // Check if the asset already exists and delete it if it does + const { data: assets } = await github.rest.repos.listReleaseAssets({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: release_id, + }); + + const existingAsset = assets.find(asset => asset.name === file); + if (existingAsset) { + await github.rest.repos.deleteReleaseAsset({ + owner: context.repo.owner, + repo: context.repo.repo, + asset_id: existingAsset.id, + }); + } + + // Upload the new asset + await github.rest.repos.uploadReleaseAsset({ + url: upload_url, + headers: { + 'content-type': 'application/octet-stream', + 'content-length': fileStat.size, + }, + name: file, + file: fs.createReadStream(filePath), + }); + + console.log(`Uploaded asset: ${file}`); + } catch (error) { + console.error(`Error processing file ${file}: ${error.message}`); + } + } \ No newline at end of file diff --git a/scripts/pre-release-checks.py b/scripts/pre-release-checks.py index cf4bbbd6..21d88fb1 100755 --- a/scripts/pre-release-checks.py +++ b/scripts/pre-release-checks.py @@ -68,6 +68,10 @@ top_version_found = True else: break + # Skip from writing the header lines "## Changelog" in the release notes. + if line.startswith('# Changelog'): + continue + release_notes.append(line) if not top_version_found: print(f"Error: No entry found in CHANGELOG.md for version {version}.")