diff --git a/.github/workflows/changelog-comment.yml b/.github/workflows/changelog-comment.yml
new file mode 100644
index 000000000..a96667c92
--- /dev/null
+++ b/.github/workflows/changelog-comment.yml
@@ -0,0 +1,121 @@
+name: Changelog Comment
+
+on:
+ pull_request:
+ types: [opened, reopened]
+ workflow_dispatch:
+ inputs:
+ pr_number:
+ description: 'PR number to post the changelog comment on (for testing)'
+ required: true
+ type: number
+
+jobs:
+ comment:
+ name: Post changelog comment
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: 💬 Post or update changelog comment
+ uses: actions/github-script@v7
+ with:
+ github-token: ${{ secrets.CROWDIN_GH_TOKEN }}
+ script: |
+ const marker = '';
+ const mergedMarker = '';
+
+ const sections = ['### Added', '', '### Changed', '', '### Deprecated', '', '### Removed', '', '### Fixed', '', '### Security'].join('\n');
+ const productBlock = (name) => `\n${name}
\n\n${sections}\n\n `;
+
+ const template = [
+ marker,
+ '## Pull request changelog',
+ '',
+ '',
+ '',
+ productBlock('App'),
+ '',
+ productBlock('Website'),
+ '',
+ productBlock('Hosting'),
+ ].join('\n');
+
+ // Resolve PR number from event or workflow_dispatch input
+ const prNumber = context.payload.pull_request?.number
+ ?? parseInt('${{ github.event.inputs.pr_number }}', 10);
+
+ if (!prNumber || isNaN(prNumber)) {
+ core.setFailed('Could not determine PR number');
+ return;
+ }
+
+ // Get PR details (need base ref for child PR detection)
+ const { data: pr } = await github.rest.pulls.get({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ pull_number: prNumber,
+ });
+
+ // Check if bot comment already exists
+ const { data: comments } = await github.rest.issues.listComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: prNumber,
+ });
+
+ const existingComment = comments.find(c => c.body.includes(marker));
+ if (existingComment) {
+ core.info('Changelog comment already exists, skipping');
+ return;
+ }
+
+ // Post the template comment
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: prNumber,
+ body: template,
+ });
+
+ core.info(`Posted changelog comment on PR #${prNumber}`);
+
+ // Detect child PR: check if this PR's base branch is another open PR's head branch
+ const baseRef = pr.base.ref;
+
+ if (baseRef === 'main' || baseRef === 'prod') {
+ return;
+ }
+
+ // Look for a parent PR whose head branch matches our base branch
+ const { data: candidatePRs } = await github.rest.pulls.list({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ state: 'open',
+ head: `${context.repo.owner}:${baseRef}`,
+ });
+
+ if (candidatePRs.length === 0) {
+ return;
+ }
+
+ const parentPR = candidatePRs[0];
+ core.info(`Detected parent PR #${parentPR.number} for child PR #${prNumber}`);
+
+ // Add admonition to child PR's changelog comment
+ const { data: childComments } = await github.rest.issues.listComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: prNumber,
+ });
+
+ const childChangelogComment = childComments.find(c => c.body.includes(marker));
+ if (childChangelogComment && !childChangelogComment.body.includes(mergedMarker)) {
+ await github.rest.issues.updateComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: childChangelogComment.id,
+ body: `${mergedMarker}\n> [!NOTE]\n> This changelog has been merged into the changelog for #${parentPR.number}\n\n${childChangelogComment.body}`,
+ });
+ }
diff --git a/.github/workflows/frontend-preview.yml b/.github/workflows/frontend-preview.yml
index e75a7122b..eecd219f3 100644
--- a/.github/workflows/frontend-preview.yml
+++ b/.github/workflows/frontend-preview.yml
@@ -50,14 +50,15 @@ jobs:
uses: peter-evans/find-comment@v3
id: fc
with:
+ token: ${{ secrets.CROWDIN_GH_TOKEN }}
issue-number: ${{ github.event.pull_request.number }}
- comment-author: 'github-actions[bot]'
body-includes: Frontend previews
- name: Comment deploy URL on PR
if: github.event_name == 'pull_request'
uses: peter-evans/create-or-update-comment@v5
with:
+ token: ${{ secrets.CROWDIN_GH_TOKEN }}
issue-number: ${{ github.event.pull_request.number }}
comment-id: ${{ steps.fc.outputs.comment-id }}
body: |
diff --git a/.github/workflows/theseus-release.yml b/.github/workflows/theseus-release.yml
index 6cd2be01f..e3319f259 100644
--- a/.github/workflows/theseus-release.yml
+++ b/.github/workflows/theseus-release.yml
@@ -1,47 +1,43 @@
name: Modrinth App release
on:
- workflow_dispatch:
- inputs:
- version-tag:
- description: Version tag to release to the wide public
- type: string
- required: true
- release-notes:
- description: Release notes to include in the Tauri version manifest
- default: A new release of the Modrinth App is available!
- type: string
- required: true
+ workflow_run:
+ workflows: ['Modrinth App build']
+ types: [completed]
jobs:
release:
name: Release Modrinth App
+ if: >-
+ github.event.workflow_run.conclusion == 'success' &&
+ startsWith(github.event.workflow_run.head_branch, 'v')
runs-on: ubuntu-latest
env:
+ VERSION_TAG: ${{ github.event.workflow_run.head_branch }}
LINUX_X64_BUNDLE_ARTIFACT_NAME: App bundle (x86_64-unknown-linux-gnu)
WINDOWS_X64_BUNDLE_ARTIFACT_NAME: App bundle (x86_64-pc-windows-msvc)
MACOS_UNIVERSAL_BUNDLE_ARTIFACT_NAME: App bundle (universal-apple-darwin)
LAUNCHER_FILES_BUCKET_BASE_URL: https://launcher-files.modrinth.com
steps:
+ - name: 📥 Check out code
+ uses: actions/checkout@v4
+
- name: 📥 Download Modrinth App artifacts
uses: dawidd6/action-download-artifact@v11
with:
workflow: theseus-build.yml
workflow_conclusion: success
event: push
- branch: ${{ inputs.version-tag }}
+ branch: ${{ env.VERSION_TAG }}
use_unzip: true
- name: 🛠️ Generate version manifest
- env:
- VERSION_TAG: ${{ inputs.version-tag }}
- RELEASE_NOTES: ${{ inputs.release-notes }}
run: |
# Reference: https://tauri.app/plugin/updater/#server-support
jq -nc \
--arg versionTag "${VERSION_TAG#v}" \
- --arg releaseNotes "$RELEASE_NOTES" \
+ --arg releaseNotes "See the full changelog at https://modrinth.com/news/changelog" \
--rawfile macOsAarch64UpdateArtifactSignature "${MACOS_UNIVERSAL_BUNDLE_ARTIFACT_NAME}/universal-apple-darwin/release/bundle/macos/Modrinth App.app.tar.gz.sig" \
--rawfile macOsX64UpdateArtifactSignature "${MACOS_UNIVERSAL_BUNDLE_ARTIFACT_NAME}/universal-apple-darwin/release/bundle/macos/Modrinth App.app.tar.gz.sig" \
--rawfile linuxX64UpdateArtifactSignature "${LINUX_X64_BUNDLE_ARTIFACT_NAME}/release/bundle/appimage/Modrinth App_${VERSION_TAG#v}_amd64.AppImage.tar.gz.sig" \
@@ -83,7 +79,6 @@ jobs:
- name: 📤 Upload release artifacts
env:
- VERSION_TAG: ${{ inputs.version-tag }}
AWS_ACCESS_KEY_ID: ${{ secrets.LAUNCHER_FILES_BUCKET_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.LAUNCHER_FILES_BUCKET_SECRET_ACCESS_KEY }}
AWS_BUCKET: ${{ secrets.LAUNCHER_FILES_BUCKET_NAME }}
@@ -116,3 +111,18 @@ jobs:
done
aws s3 cp updates.json "s3://${AWS_BUCKET}"
+
+ - name: 🏷️ Create GitHub release
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ VERSION="${VERSION_TAG#v}"
+
+ gh release create "$VERSION_TAG" \
+ --title "Modrinth App ${VERSION}" \
+ --notes "See the full changelog at https://modrinth.com/news/changelog" \
+ "${WINDOWS_X64_BUNDLE_ARTIFACT_NAME}/release/bundle/nsis/Modrinth App_${VERSION}_x64-setup.exe" \
+ "${MACOS_UNIVERSAL_BUNDLE_ARTIFACT_NAME}/universal-apple-darwin/release/bundle/dmg/Modrinth App_${VERSION}_universal.dmg" \
+ "${LINUX_X64_BUNDLE_ARTIFACT_NAME}/release/bundle/appimage/Modrinth App_${VERSION}_amd64.AppImage" \
+ "${LINUX_X64_BUNDLE_ARTIFACT_NAME}/release/bundle/deb/Modrinth App_${VERSION}_amd64.deb" \
+ "${LINUX_X64_BUNDLE_ARTIFACT_NAME}/release/bundle/rpm/Modrinth App-${VERSION}-1.x86_64.rpm"
diff --git a/apps/frontend/nuxt.config.ts b/apps/frontend/nuxt.config.ts
index 56ed1b5cc..693693853 100644
--- a/apps/frontend/nuxt.config.ts
+++ b/apps/frontend/nuxt.config.ts
@@ -115,7 +115,7 @@ export default defineNuxtConfig({
await import('./src/templates/docs/index.ts').then((m) => m.default),
)
const blogArticles = await import('@modrinth/blog').then((m) => m.articles)
- const { getChangelog } = await import('@modrinth/utils')
+ const { getChangelog } = await import('@modrinth/blog')
nitroConfig.prerender = nitroConfig.prerender || {}
nitroConfig.prerender.routes = nitroConfig.prerender.routes || []
diff --git a/apps/frontend/src/pages/news/changelog/[product]/[date].vue b/apps/frontend/src/pages/news/changelog/[product]/[date].vue
index 931419d68..7005fe0ab 100644
--- a/apps/frontend/src/pages/news/changelog/[product]/[date].vue
+++ b/apps/frontend/src/pages/news/changelog/[product]/[date].vue
@@ -1,7 +1,7 @@