diff --git a/.github/workflows/changelog.yaml b/.github/workflows/changelog.yaml new file mode 100644 index 0000000000..c785b09e1b --- /dev/null +++ b/.github/workflows/changelog.yaml @@ -0,0 +1,85 @@ +on: + workflow_dispatch: + inputs: + channel: + description: "Release channel" + required: true + type: choice + options: + - nightly + - stable + workflow_call: + inputs: + channel: + required: true + type: string + issue_number: + required: false + type: number + comment_id: + required: false + type: number + +permissions: + contents: write + pull-requests: write + +jobs: + generate: + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - if: inputs.comment_id + uses: ./.github/actions/add-reaction + with: + token: ${{ secrets.GITHUB_TOKEN }} + repo: ${{ github.repository }} + comment: ${{ inputs.comment_id }} + reaction: eyes + - id: version + run: | + CHANNEL="${{ inputs.channel }}" + if [ "$CHANNEL" = "nightly" ]; then + LATEST_TAG=$(git tag -l 'desktop_v*-nightly.*' --sort=-v:refname | head -n1) + PREV_REF="main" + else + LATEST_TAG=$(git tag -l 'desktop_v*' --sort=-v:refname | grep -v 'nightly' | head -n1) + PREV_REF=$(git tag -l 'desktop_v*-nightly.*' --sort=-v:refname | head -n1) + fi + VERSION="${LATEST_TAG#desktop_v}" + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT + echo "prev_ref=$PREV_REF" >> $GITHUB_OUTPUT + - run: | + curl -fsSL https://opencode.ai/install | bash + echo "$HOME/.opencode/bin" >> $GITHUB_PATH + - run: | + VERSION="${{ steps.version.outputs.version }}" + CHANGELOG_FILE="apps/web/content/changelog/${VERSION}.mdx" + + opencode -p "Create ${CHANGELOG_FILE} for commits between ${{ steps.version.outputs.prev_ref }} and ${{ steps.version.outputs.latest_tag }}. Follow apps/web/content/changelog/AGENTS.md." + - uses: peter-evans/create-pull-request@v8 + id: cpr + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: changelog/${{ steps.version.outputs.version }} + title: "docs: changelog for ${{ steps.version.outputs.version }}" + body: | + Auto-generated changelog for version ${{ steps.version.outputs.version }}. + + Generated using `opencode` CLI based on commits from ${{ steps.version.outputs.prev_ref }} to ${{ steps.version.outputs.latest_tag }}. + commit-message: "docs: add changelog for ${{ steps.version.outputs.version }}" + delete-branch: true + - if: inputs.issue_number + uses: ./.github/actions/comment-result + with: + token: ${{ secrets.GITHUB_TOKEN }} + repo: ${{ github.repository }} + issue_number: ${{ inputs.issue_number }} + data: | + [{"header": "Version", "value": "`${{ steps.version.outputs.version }}`"}, {"header": "PR", "value": "${{ steps.cpr.outputs.pull-request-url || 'No changes' }}"}] diff --git a/.github/workflows/command_dispatcher.yaml b/.github/workflows/command_dispatcher.yaml index 87c23f07e1..9477a08501 100644 --- a/.github/workflows/command_dispatcher.yaml +++ b/.github/workflows/command_dispatcher.yaml @@ -74,3 +74,30 @@ jobs: issue_number: ${{ github.event.issue.number }} comment_id: ${{ github.event.comment.id }} secrets: inherit + + parse-changelog-channel: + needs: check-permission + if: needs.check-permission.outputs.allowed == 'true' && contains(github.event.comment.body, '/changelog') + runs-on: ubuntu-latest + outputs: + channel: ${{ steps.parse.outputs.channel }} + steps: + - id: parse + env: + COMMENT: ${{ github.event.comment.body }} + run: | + if [[ "$COMMENT" =~ /changelog[[:space:]]+(nightly|stable)($|[[:space:]]) ]]; then + echo "channel=${BASH_REMATCH[1]}" >> $GITHUB_OUTPUT + else + echo "channel=nightly" >> $GITHUB_OUTPUT + fi + + handle-changelog: + needs: [check-permission, parse-changelog-channel] + if: needs.check-permission.outputs.allowed == 'true' && contains(github.event.comment.body, '/changelog') + uses: ./.github/workflows/changelog.yaml + with: + channel: ${{ needs.parse-changelog-channel.outputs.channel }} + issue_number: ${{ github.event.issue.number }} + comment_id: ${{ github.event.comment.id }} + secrets: inherit diff --git a/.github/workflows/handle_release.yaml b/.github/workflows/handle_release.yaml index 867b289ea4..274811095f 100644 --- a/.github/workflows/handle_release.yaml +++ b/.github/workflows/handle_release.yaml @@ -25,6 +25,10 @@ jobs: REPO: ${{ github.repository }} steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + - run: git fetch --tags --force - uses: ./.github/actions/add-reaction with: token: ${{ secrets.GITHUB_TOKEN }} @@ -33,28 +37,48 @@ jobs: reaction: eyes - id: resolve run: | - PR_DATA=$(gh api repos/$REPO/pulls/${{ inputs.issue_number }} 2>/dev/null) || { - echo "merged=false" >> $GITHUB_OUTPUT - echo "error=Not a pull request or PR not found" >> $GITHUB_OUTPUT - exit 0 - } + if [ "${{ inputs.channel }}" == "stable" ]; then + NIGHTLY_TAG=$(git tag -l 'desktop_v*-nightly.*' --sort=-v:refname | head -n1) + if [ -z "$NIGHTLY_TAG" ]; then + echo "resolved=false" >> $GITHUB_OUTPUT + echo "error=No nightly release tag found" >> $GITHUB_OUTPUT + exit 0 + fi - MERGED=$(echo "$PR_DATA" | jq -r '.merged') - if [ "$MERGED" != "true" ]; then - echo "merged=false" >> $GITHUB_OUTPUT - echo "error=PR must be merged first" >> $GITHUB_OUTPUT - exit 0 - fi + TAG_SHA=$(git rev-list -n 1 "$NIGHTLY_TAG") + TEMP_BRANCH="release-temp-${TAG_SHA:0:8}" - MERGE_SHA=$(echo "$PR_DATA" | jq -r '.merge_commit_sha') - TEMP_BRANCH="release-temp-${MERGE_SHA:0:8}" + GH_TOKEN=${{ secrets.PAT_TOKEN }} gh api repos/$REPO/git/refs -f ref="refs/heads/$TEMP_BRANCH" -f sha="$TAG_SHA" + + echo "resolved=true" >> $GITHUB_OUTPUT + echo "target_sha=$TAG_SHA" >> $GITHUB_OUTPUT + echo "target_ref=$NIGHTLY_TAG" >> $GITHUB_OUTPUT + echo "branch=$TEMP_BRANCH" >> $GITHUB_OUTPUT + else + PR_DATA=$(gh api repos/$REPO/pulls/${{ inputs.issue_number }} 2>/dev/null) || { + echo "resolved=false" >> $GITHUB_OUTPUT + echo "error=Not a pull request or PR not found" >> $GITHUB_OUTPUT + exit 0 + } - GH_TOKEN=${{ secrets.PAT_TOKEN }} gh api repos/$REPO/git/refs -f ref="refs/heads/$TEMP_BRANCH" -f sha="$MERGE_SHA" + MERGED=$(echo "$PR_DATA" | jq -r '.merged') + if [ "$MERGED" != "true" ]; then + echo "resolved=false" >> $GITHUB_OUTPUT + echo "error=PR must be merged first" >> $GITHUB_OUTPUT + exit 0 + fi - echo "merged=true" >> $GITHUB_OUTPUT - echo "merge_sha=$MERGE_SHA" >> $GITHUB_OUTPUT - echo "branch=$TEMP_BRANCH" >> $GITHUB_OUTPUT - - if: steps.resolve.outputs.merged == 'true' + MERGE_SHA=$(echo "$PR_DATA" | jq -r '.merge_commit_sha') + TEMP_BRANCH="release-temp-${MERGE_SHA:0:8}" + + GH_TOKEN=${{ secrets.PAT_TOKEN }} gh api repos/$REPO/git/refs -f ref="refs/heads/$TEMP_BRANCH" -f sha="$MERGE_SHA" + + echo "resolved=true" >> $GITHUB_OUTPUT + echo "target_sha=$MERGE_SHA" >> $GITHUB_OUTPUT + echo "target_ref=$MERGE_SHA" >> $GITHUB_OUTPUT + echo "branch=$TEMP_BRANCH" >> $GITHUB_OUTPUT + fi + - if: steps.resolve.outputs.resolved == 'true' id: trigger uses: ./.github/actions/trigger-workflow with: @@ -67,26 +91,28 @@ jobs: - id: result run: | CHANNEL="${{ inputs.channel }}" - MERGED="${{ steps.resolve.outputs.merged }}" - MERGE_SHA="${{ steps.resolve.outputs.merge_sha }}" + RESOLVED="${{ steps.resolve.outputs.resolved }}" + TARGET_SHA="${{ steps.resolve.outputs.target_sha }}" + TARGET_REF="${{ steps.resolve.outputs.target_ref }}" + ERROR="${{ steps.resolve.outputs.error }}" RUN_ID="${{ steps.trigger.outputs.run_id }}" - if [ "$MERGED" != "true" ]; then + if [ "$RESOLVED" != "true" ]; then DATA=$(jq -cn \ --arg channel "\`$CHANNEL\`" \ --arg sha "-" \ - --arg status "PR must be merged first" \ + --arg status "$ERROR" \ '[{header: "Channel", value: $channel}, {header: "Commit", value: $sha}, {header: "Status", value: $status}]') elif [ -n "$RUN_ID" ]; then DATA=$(jq -cn \ --arg channel "\`$CHANNEL\`" \ - --arg sha "[\`${MERGE_SHA:0:8}\`](https://github.com/$REPO/commit/$MERGE_SHA)" \ + --arg sha "[\`${TARGET_SHA:0:8}\`](https://github.com/$REPO/commit/$TARGET_SHA) ($TARGET_REF)" \ --arg status "[View](https://github.com/$REPO/actions/runs/$RUN_ID)" \ '[{header: "Channel", value: $channel}, {header: "Commit", value: $sha}, {header: "Status", value: $status}]') else DATA=$(jq -cn \ --arg channel "\`$CHANNEL\`" \ - --arg sha "[\`${MERGE_SHA:0:8}\`](https://github.com/$REPO/commit/$MERGE_SHA)" \ + --arg sha "[\`${TARGET_SHA:0:8}\`](https://github.com/$REPO/commit/$TARGET_SHA) ($TARGET_REF)" \ --arg status "[Logs](https://github.com/$REPO/actions/runs/${{ github.run_id }})" \ '[{header: "Channel", value: $channel}, {header: "Commit", value: $sha}, {header: "Status", value: $status}]') fi @@ -97,7 +123,7 @@ jobs: repo: ${{ github.repository }} issue_number: ${{ inputs.issue_number }} data: ${{ steps.result.outputs.data }} - - if: steps.resolve.outputs.merged == 'true' + - if: steps.resolve.outputs.resolved == 'true' env: GH_TOKEN: ${{ secrets.PAT_TOKEN }} run: gh api repos/${{ github.repository }}/git/refs/heads/${{ steps.resolve.outputs.branch }} -X DELETE || true