diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b734ed19ca3..736e20cff52 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,7 @@ on: - 'linux-release-*' tags: - 'release-*.*.*-linux*' + - 'release-*.*.*-test*' pull_request: branches: - linux @@ -198,20 +199,6 @@ jobs: - name: Package application run: yarn run package if: ${{ matrix.friendlyName == 'Ubuntu' }} - - name: Create Release - uses: softprops/action-gh-release@v1 - if: - ${{ matrix.friendlyName == 'Ubuntu' && startsWith(github.ref, - 'refs/tags/') }} - with: - files: | - dist/*.AppImage - dist/*.deb - dist/*.rpm - dist/*.txt - draft: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload output artifacts uses: actions/upload-artifact@v3 if: matrix.friendlyName == 'Ubuntu' @@ -221,4 +208,62 @@ jobs: dist/*.AppImage dist/*.deb dist/*.rpm + dist/*.sha256 retention-days: 5 + + publish: + name: Create GitHub release + needs: [build, lint] + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') + permissions: + contents: write + steps: + - uses: actions/checkout@v3 + + - name: Download all artifacts + uses: actions/download-artifact@v2 + with: + path: './artifacts' + + - name: Display structure of downloaded files + run: ls -R + working-directory: './artifacts' + + - name: Get tag name without prefix + run: | + RELEASE_TAG=${GITHUB_REF/refs\/tags\//} + echo "RELEASE_TAG=${RELEASE_TAG}" >> $GITHUB_ENV + tagNameWithoutPrefix="${RELEASE_TAG:1}" + echo "RELEASE_TAG_WITHOUT_PREFIX=${tagNameWithoutPrefix}" >> $GITHUB_ENV + + # TODO: generate release notes + # - pull in default if version matches X.Y.Z-linux1 + # - otherwise stub template + + - name: Generate release notes + run: | + yarn + node -r ts-node/register script/generate-release-notes.ts "${{ github.workspace }}/artifacts" "${{ env.RELEASE_TAG_WITHOUT_PREFIX }}" + RELEASE_NOTES_FILE=script/release_notes.txt + if [[ ! -f "$RELEASE_NOTES_FILE" ]]; then + echo "$RELEASE_NOTES_FILE does not exist. Something might have gone wrong while generating the release notes." + exit 1 + fi + echo "Release notes:" + echo "---" + cat ${RELEASE_NOTES_FILE} + echo "---" + + - name: Create Release + uses: softprops/action-gh-release@v1 + with: + name: GitHub Desktop for Linux ${{ env.RELEASE_TAG_WITHOUT_PREFIX }} + body_path: script/release_notes.txt + files: | + artifacts/*.AppImage + artifacts/*.deb + artifacts/*.rpm + draft: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 30a0037ef0b..4b8a3b69fe7 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ app/node_modules/ junit*.xml *.swp tslint-rules/ +script/release_notes.txt diff --git a/script/generate-release-notes.ts b/script/generate-release-notes.ts new file mode 100644 index 00000000000..b52d4f622a4 --- /dev/null +++ b/script/generate-release-notes.ts @@ -0,0 +1,140 @@ +/* eslint-disable no-sync */ + +const glob = require('glob') +const { basename } = require('path') +const fs = require('fs') + +type ChecksumEntry = { filename: string; checksum: string } + +type ChecksumGroups = Record<'x64' | 'arm' | 'arm64', Array> + +// 3 architectures * 3 package formats * 2 files (package + checksum file) +const SUCCESSFUL_RELEASE_FILE_COUNT = 3 * 3 * 2 + +const Glob = glob.GlobSync + +const args = process.argv.slice(2) +const artifactsDir = args[0] + +if (!artifactsDir) { + console.error( + `🔴 First parameter with artifacts directory not found. Aborting...` + ) + process.exit(1) +} + +const releaseTagWithoutPrefix = args[1] +if (!releaseTagWithoutPrefix) { + console.error(`🔴 Second parameter with release tag not found. Aborting...`) + process.exit(1) +} + +console.log( + `Preparing release notes for release tag ${releaseTagWithoutPrefix}` +) + +const files = new Glob(artifactsDir + '/**/*', { nodir: true }) + +let countFiles = 0 +const shaEntries = new Array() + +for (const file of files.found) { + if (file.endsWith('.sha256')) { + shaEntries.push(getShaContents(file)) + } + + countFiles++ +} + +if (SUCCESSFUL_RELEASE_FILE_COUNT !== countFiles) { + console.error( + `🔴 Artifacts folder has ${countFiles} assets, expecting ${SUCCESSFUL_RELEASE_FILE_COUNT}. Please check the GH Actions artifacts to see which are missing.` + ) + process.exit(1) +} + +const shaEntriesByArchitecture: ChecksumGroups = { + x64: shaEntries.filter( + e => + e.filename.includes('-linux-x86_64-') || + e.filename.includes('-linux-amd64-') + ), + arm: shaEntries.filter( + e => + e.filename.includes('-linux-armv7l-') || + e.filename.includes('-linux-armhf-') + ), + arm64: shaEntries.filter( + e => + e.filename.includes('-linux-aarch64-') || + e.filename.includes('-linux-arm64-') + ), +} + +console.log(`Found ${countFiles} files in artifacts directory`) +console.log(shaEntriesByArchitecture) + +const draftReleaseNotes = generateDraftReleaseNotes( + [], + shaEntriesByArchitecture +) +const releaseNotesPath = __dirname + '/release_notes.txt' + +fs.writeFileSync(releaseNotesPath, draftReleaseNotes, { encoding: 'utf8' }) + +console.log( + `✅ All done! The release notes have been written to ${releaseNotesPath}` +) + +/** + * Returns the filename (excluding .sha256) and its contents (a SHA256 checksum). + */ +function getShaContents(filePath: string): { + filename: string + checksum: string +} { + const filename = basename(filePath).slice(0, -7) + const checksum = fs.readFileSync(filePath, 'utf8') + + return { filename, checksum } +} + +function formatEntry(e: ChecksumEntry): string { + return `**${e.filename}**\n${e.checksum}\n` +} + +/** + * Takes the release notes entries and the SHA entries, then merges them into the full draft release notes ✨ + */ +function generateDraftReleaseNotes( + releaseNotesEntries: Array, + shaEntries: ChecksumGroups +): string { + const changelogText = releaseNotesEntries.join('\n') + + const x64Section = shaEntries.x64.map(formatEntry).join('\n') + const armSection = shaEntries.arm.map(formatEntry).join('\n') + const arm64Section = shaEntries.arm64.map(formatEntry).join('\n') + + const draftReleaseNotes = `${changelogText} + +## Fixes and improvements + +TODO + +## SHA-256 checksums + +### x64 + +${x64Section} + +### ARM64 + +${arm64Section} + +### ARM + +${armSection}` + + return draftReleaseNotes +} diff --git a/script/package.ts b/script/package.ts index 38075a7eccd..0aec32ee541 100644 --- a/script/package.ts +++ b/script/package.ts @@ -182,6 +182,9 @@ async function generateChecksums(files: Array) { for (const [fullPath, checksum] of checksums) { const fileName = path.basename(fullPath) checksumsText += `${checksum} - ${fileName}\n` + + const checksumFilePath = `${fullPath}.sha256` + await writeFile(checksumFilePath, checksum) } const checksumFile = path.join(distRoot, 'checksums.txt')