diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml new file mode 100644 index 0000000..28af090 --- /dev/null +++ b/.github/workflows/post-release.yml @@ -0,0 +1,120 @@ +# The way this works is the following: +# +# The create-release job runs purely to initialize the GitHub release itself +# and to output upload_url for the following job. +# +# The build-release job runs only once create-release is finished. It gets the +# release upload URL from create-release job outputs, then builds the release +# executables for each supported platform and attaches them as release assets +# to the previously created release. +# +# The key here is that we create the release only once. +# +# Reference: +# https://eugene-babichenko.github.io/blog/2020/05/09/github-actions-cross-platform-auto-releases/ + +name: post-release +on: + push: + tags: + - "v*" +env: + BIN_NAME: typos +jobs: + create-release: + name: create-release + runs-on: ubuntu-latest + outputs: + upload_url: ${{ steps.release.outputs.upload_url }} + release_version: ${{ env.RELEASE_VERSION }} + steps: + - name: Get the release version from the tag + shell: bash + if: env.RELEASE_VERSION == '' + run: | + # See: https://github.community/t5/GitHub-Actions/How-to-get-just-the-tag-name/m-p/32167/highlight/true#M1027 + echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV + echo "version is: ${{ env.RELEASE_VERSION }}" + - name: Checkout repository + uses: actions/checkout@v2 + with: + fetch-depth: 1 + - name: Generate Release Notes + run: | + ./.github/workflows/release-notes.py --tag ${{ env.RELEASE_VERSION }} --output notes-${{ env.RELEASE_VERSION }}.md + cat notes-${{ env.RELEASE_VERSION }}.md + - name: Create GitHub release + id: release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.RELEASE_VERSION }} + release_name: ${{ env.RELEASE_VERSION }} + body_path: notes-${{ env.RELEASE_VERSION }}.md + build-release: + name: build-release + needs: create-release + strategy: + matrix: + build: [linux, macos, win-msvc] + include: + - build: linux + os: ubuntu-18.04 + rust: stable + target: x86_64-unknown-linux-musl + - build: macos + os: macos-latest + rust: stable + target: x86_64-apple-darwin + - build: win-msvc + os: windows-2019 + rust: stable + target: x86_64-pc-windows-msvc + runs-on: ${{ matrix.os }} + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + fetch-depth: 1 + - name: Install packages (Ubuntu) + if: matrix.os == 'ubuntu-18.04' + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends xz-utils liblz4-tool musl-tools + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + profile: minimal + override: true + target: ${{ matrix.target }} + - name: Build release binary + run: cargo build --target ${{ matrix.target }} --verbose --release + - name: Build archive + shell: bash + run: | + outdir="./target/${{ env.TARGET_DIR }}/release" + staging="${{ env.BIN_NAME }}-${{ needs.create-release.outputs.release_version }}-${{ matrix.target }}" + mkdir -p "$staging"/{complete,doc} + cp {README.md,LICENSE-*} "$staging/" + cp {CHANGELOG.md,docs/*} "$staging/doc/" + if [ "${{ matrix.os }}" = "windows-2019" ]; then + cp "target/${{ matrix.target }}/release/${{ env.BIN_NAME }}.exe" "$staging/" + cd "$staging" + 7z a "../$staging.zip" . + echo "ASSET=$staging.zip" >> $GITHUB_ENV + else + cp "target/${{ matrix.target }}/release/${{ env.BIN_NAME }}" "$staging/" + tar czf "$staging.tar.gz" -C "$staging" . + echo "ASSET=$staging.tar.gz" >> $GITHUB_ENV + fi + - name: Upload release archive + uses: actions/upload-release-asset@v1.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create-release.outputs.upload_url }} + asset_path: ${{ env.ASSET }} + asset_name: ${{ env.ASSET }} + asset_content_type: application/octet-stream diff --git a/.github/workflows/release-notes.py b/.github/workflows/release-notes.py new file mode 100755 index 0000000..7a0d26d --- /dev/null +++ b/.github/workflows/release-notes.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +import argparse +import re +import pathlib +import sys + + +_STDIO = pathlib.Path("-") + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-i", "--input", type=pathlib.Path, default="CHANGELOG.md") + parser.add_argument("--tag", required=True) + parser.add_argument("-o", "--output", type=pathlib.Path, required=True) + args = parser.parse_args() + + if args.input == _STDIO: + lines = sys.stdin.readlines() + else: + with args.input.open() as fh: + lines = fh.readlines() + version = args.tag.lstrip("v") + + note_lines = [] + for line in lines: + if line.startswith("## ") and version in line: + note_lines.append(line) + elif note_lines and line.startswith("## "): + break + elif note_lines: + note_lines.append(line) + + notes = "".join(note_lines).strip() + if args.output == _STDIO: + print(notes) + else: + args.output.write_text(notes) + + +if __name__ == "__main__": + main() diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2b9b730..4a72f68 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -51,91 +51,6 @@ stages: $(Build.StagingDirectory)/tools/committed HEAD~..HEAD^2 --no-merge-commit -vv displayName: Lint commit history condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest')) -- stage: release - displayName: Release - dependsOn: [] - condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/') - jobs: - - job: - strategy: - matrix: - windows: - imageName: ${{ variables.windows_vm }} - target: 'x86_64-pc-windows-msvc' - crate_name: typos - mac: - imageName: ${{ variables.mac_vm }} - target: 'x86_64-apple-darwin' - crate_name: typos - linux: - imageName: ${{ variables.linux_vm }} - target: 'x86_64-unknown-linux-gnu' - crate_name: typos - pool: - vmImage: $(imageName) - steps: - - template: install-rust.yml@templates - parameters: - rust: stable - targets: ["$(TARGET)"] - - script: | - cargo build --target $(TARGET) --release --bin $(CRATE_NAME) - displayName: Build - - task: CopyFiles@2 - displayName: Stage assets - condition: ne( variables['Agent.OS'], 'Windows_NT' ) - inputs: - sourceFolder: '$(Build.SourcesDirectory)/target/$(TARGET)/release' - contents: $(crate_name) - targetFolder: '$(Build.BinariesDirectory)/' - - task: CopyFiles@2 - displayName: Stage assets on Windows - condition: eq( variables['Agent.OS'], 'Windows_NT' ) - inputs: - sourceFolder: '$(Build.SourcesDirectory)/target/$(TARGET)/release' - contents: $(crate_name).exe - targetFolder: '$(Build.BinariesDirectory)/' - - task: ArchiveFiles@2 - displayName: Tarball assets - condition: ne( variables['Agent.OS'], 'Windows_NT' ) - inputs: - rootFolderOrFile: '$(Build.BinariesDirectory)/$(crate_name)' - archiveType: 'tar' - tarCompression: 'gz' - archiveFile: '$(Build.ArtifactStagingDirectory)/$(crate_name)-$(Build.SourceBranchName)-$(TARGET).tar.gz' - - task: GithubRelease@0 - condition: ne( variables['Agent.OS'], 'Windows_NT' ) - inputs: - gitHubConnection: 'crate-ci-token' - repositoryName: 'crate-ci/typos' - action: 'edit' - target: '$(build.sourceVersion)' - tagSource: 'manual' - tag: '$(Build.SourceBranchName)' - assets: '$(Build.ArtifactStagingDirectory)/$(crate_name)-$(Build.SourceBranchName)-$(TARGET).tar.gz' - title: '$(Build.SourceBranchName)' - assetUploadMode: 'replace' - addChangeLog: true - - task: ArchiveFiles@2 - displayName: Zip assets - condition: eq( variables['Agent.OS'], 'Windows_NT' ) - inputs: - rootFolderOrFile: '$(Build.BinariesDirectory)/$(crate_name).exe' - archiveType: 'zip' - archiveFile: '$(Build.ArtifactStagingDirectory)/$(crate_name)-$(Build.SourceBranchName)-$(TARGET).zip' - - task: GithubRelease@0 - condition: eq( variables['Agent.OS'], 'Windows_NT' ) - inputs: - gitHubConnection: 'crate-ci-token' - repositoryName: 'crate-ci/typos' - action: 'edit' - target: '$(build.sourceVersion)' - tagSource: 'manual' - tag: '$(Build.SourceBranchName)' - assets: '$(Build.ArtifactStagingDirectory)/$(crate_name)-$(Build.SourceBranchName)-$(TARGET).zip' - title: '$(Build.SourceBranchName)' - assetUploadMode: 'replace' - addChangeLog: true resources: repositories: