Skip to content

Commit

Permalink
Automate publishing new releases on crates.io
Browse files Browse the repository at this point in the history
These scripts will help automate the steps required to publish new
releases on crates.io:

- update version number in `Cargo.toml`, `README.md`, and `src/lib.rs`

- update changelog in `README.md`

- run `cargo publish` if all tests pass
  • Loading branch information
mgeisler committed Jul 29, 2020
1 parent 49d7c96 commit 37fcdf5
Show file tree
Hide file tree
Showing 2 changed files with 216 additions and 0 deletions.
153 changes: 153 additions & 0 deletions .github/workflows/prepare-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
name: Prepare Release PR

on:
push:
branches:
- 'release-*'

jobs:
setup:
runs-on: ubuntu-latest
outputs:
name: ${{ steps.vars.outputs.name }}
old-version: ${{ steps.vars.outputs.old-version }}
new-version: ${{ steps.vars.outputs.new-version }}
steps:
- name: Check out repository
uses: actions/checkout@v2

- name: Set variables
id: vars
run: |
NAME=$(cargo metadata -q --no-deps | jq -r '.packages[0].name')
OLD_VERSION=$(cargo metadata -q --no-deps | jq -r '.packages[0].version')
NEW_VERSION=$(echo ${{ github.ref }} | cut -d '-' -f 2-)
echo "Version from Cargo: $OLD_VERSION"
echo "Version from branch: $NEW_VERSION"
echo "::set-output name=name::$NAME"
echo "::set-output name=old-version::$OLD_VERSION"
echo "::set-output name=new-version::$NEW_VERSION"
- name: Verify version format
run: |
echo '${{ steps.vars.outputs.new-version }}' | grep -q '^[0-9]\+\.[0-9]\+\.[0-9]\+$'
pull-request:
needs: setup
if: ${{ needs.setup.outputs.old-version != needs.setup.outputs.new-version }}
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v2

- name: Configure Git user
run: |
git config user.name "Martin Geisler"
git config user.email "martin@geisler.net"
- name: Update changelog for version ${{ needs.setup.outputs.new-version }}
id: changelog
uses: actions/github-script@v2
with:
script: |
var fs = require('fs')
const old_version = '${{ needs.setup.outputs.old-version }}'
const new_version = '${{ needs.setup.outputs.new-version }}'
let cutoff = '1970-01-01'
const releases = await github.repos.listReleases(context.repo)
for (const release of releases.data) {
if (release.tag_name == old_version) {
cutoff = release.published_at
break
}
}
core.info(`Finding merged PRs after ${cutoff}`)
let q = [`repo:${context.repo.owner}/${context.repo.repo}`,
'is:pr', 'is:merged', `merged:>${cutoff}`]
const prs = await github.search.issuesAndPullRequests({
q: q.join(' '),
sort: 'created',
order: 'asc',
})
core.info(`Found ${prs.data.items.length} merged PRs`)
const changelog = prs.data.items.map(
pr => `* [#${pr.number}](${pr.html_url}): ${pr.title}`
).join('\n\n')
core.exportVariable('CHANGELOG', changelog)
var readme = fs.readFileSync('README.md', 'utf8')
const today = new Date().toISOString().split('T')[0]
const heading = `## Version ${new_version} (${today})\n`
if (readme.match('## Unreleased')) {
readme = readme.replace('## Unreleased', `${heading}\n${changelog}`)
} else {
readme = readme.replace('# Changelog', `# Changelog\n\n${heading}\n${changelog}`)
}
fs.writeFileSync('README.md', readme)
- name: Commit changelog
run: |
git commit --all -m "Update changelog for version ${{ needs.setup.outputs.new-version }}"
- name: Update TOML code blocks
run: |
import fileinput, re, sys
NAME = '${{ needs.setup.outputs.name }}'
NEW_VERSION = '${{ needs.setup.outputs.new-version }}'
MAJOR_MINOR = '.'.join(NEW_VERSION.split('.')[:2])
for line in fileinput.input(inplace=True):
line = re.sub(f'{NAME} = "[^"]+"',
f'{NAME} = "{MAJOR_MINOR}"', line)
line = re.sub(f'{NAME} = {{ version = "[^"]+"',
f'{NAME} = {{ version = "{MAJOR_MINOR}"', line)
sys.stdout.write(line)
shell: python3 {0} README.md

- name: Update html_root_url
run: |
import fileinput, re, sys
NAME = '${{ needs.setup.outputs.name }}'
NEW_VERSION = '${{ needs.setup.outputs.new-version }}'
for line in fileinput.input(inplace=True):
sys.stdout.write(
re.sub(f'html_root_url = "https://docs.rs/{NAME}/[^"]+"',
f'html_root_url = "https://docs.rs/{NAME}/{NEW_VERSION}"', line))
shell: python3 {0} src/lib.rs

- name: Update crate version to ${{ needs.setup.outputs.new-version }}
uses: thomaseizinger/set-crate-version@1.0.0
with:
version: ${{ needs.setup.outputs.new-version }}

- name: Build and test
run: |
cargo test
- name: Commit version bump
run: |
git commit --all -m "Bump version to ${{ needs.setup.outputs.new-version }}"
- name: Push version bump
run: git push origin

- name: Create pull request
uses: actions/github-script@v2
with:
script: |
const pr = await github.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
head: 'release-${{ needs.setup.outputs.new-version }}',
base: 'master',
title: 'Release ${{ needs.setup.outputs.new-version }}',
body: process.env.CHANGELOG,
})
core.info(`Created PR: ${pr.data.html_url}`)
63 changes: 63 additions & 0 deletions .github/workflows/publish-crate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Publish Crate

on:
push:
branches:
- master
paths:
- Cargo.toml
repository_dispatch:
types: publish

jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v2

- name: Set variables
id: vars
run: |
NAME=$(cargo metadata -q --no-deps | jq -r '.packages[0].name')
VERSION=$(cargo metadata -q --no-deps | jq -r '.packages[0].version')
CHANGELOG=$(awk '/^## Version/ {i++}; i==1 {print}; i>1 {exit}' README.md \
| python3 -c 'import sys, json; print(json.dumps(sys.stdin.read()))')
echo "::set-output name=name::$NAME"
echo "::set-output name=version::$VERSION"
echo "::set-output name=changelog::$CHANGELOG"
echo "Found $NAME-$VERSION"
- name: Lookup ${{ steps.vars.outputs.version }} tag
id: need-release
uses: actions/github-script@v2
with:
script: |
const version = '${{ steps.vars.outputs.version }}'
const tags = await github.repos.listTags(context.repo)
if (tags.data.some(tag => tag.name == version)) {
core.info(`Found ${version} tag -- will skip publish step`)
return false
}
core.info(`Found no ${version} tag -- will proceed with publishing`)
return true
# The result from above is JSON-encoded, meaning that we
# end up with the string 'true', not the Boolean true.
- if: steps.need-release.outputs.result == 'true'
name: Publish crate to crates.io
run: |
echo "Publishing ${{ steps.vars.outputs.name }}-${{ steps.vars.outputs.version }}"
cargo publish --token ${{ secrets.CARGO_TOKEN }}
- if: steps.need-release.outputs.result == 'true'
name: Create GitHub release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.vars.outputs.version }}
release_name: ${{ steps.vars.outputs.name }}-${{ steps.vars.outputs.version }}
body: ${{ fromJson(steps.vars.outputs.changelog) }}
draft: false
prerelease: false

0 comments on commit 37fcdf5

Please sign in to comment.