diff --git a/.github/actions/release-firmware/action.yml b/.github/actions/release-firmware/action.yml new file mode 100644 index 00000000..0ce949da --- /dev/null +++ b/.github/actions/release-firmware/action.yml @@ -0,0 +1,57 @@ +name: 'Release Firmware' +description: 'Compiles the firmware for the specified board version and uploads it to a GitHub release url' +inputs: + board_version: + required: true + url: + required: true + token: + required: true +runs: + using: "composite" + steps: + - name: Compile for ODrive ${{ inputs.board_version }} + shell: bash + run: | + # for debugging + arm-none-eabi-gcc --version + python3 --version + + cd ${{ github.workspace }}/Firmware + + echo "CONFIG_STRICT=true" > tup.config + echo "CONFIG_BOARD_VERSION=${{ inputs.board_version }}" >> tup.config + tup init + tup generate ./tup_build.sh + bash -xe ./tup_build.sh + + - name: Upload ELF file + shell: bash + run: | + urls=( ${{ inputs.url }} ) + URL="${urls[0]}=ODriveFirmware_${{ inputs.board_version }}.elf" + echo "uploading to $URL" + + curl --silent --show-error --fail \ + -H "Content-Type: application/octet-stream" \ + -H "Authorization: token ${{ inputs.token }}" \ + --data-binary @"Firmware/build/ODriveFirmware.elf" \ + "$URL" + + - name: Upload HEX file + shell: bash + run: | + urls=( ${{ inputs.url }} ) + URL="${urls[0]}=ODriveFirmware_${{ inputs.board_version }}.hex" + echo "uploading to $URL" + + curl --silent --show-error --fail \ + -H "Content-Type: application/octet-stream" \ + -H "Authorization: token ${{ inputs.token }}" \ + --data-binary @"Firmware/build/ODriveFirmware.hex" \ + "$URL" + + # This is required to make the action repeatable + - name: Clean up + shell: bash + run: git clean -dffx diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 00000000..0be5fc10 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,109 @@ +name: Create Release Draft + +on: + push: + tags: ['fw-v*'] + +jobs: + release: + runs-on: ubuntu-latest + strategy: + fail-fast: true + + steps: + - uses: actions/checkout@v2 + + - name: Parse Release Specs + id: release_specs + run: | + echo "github.ref = ${{ github.ref }}" + + function get_version() { sed -E -n "s|.*([0-9]+\.[0-9]+\.[0-9]+).*|\1|p"; } + + #LAST_VERSION="$(curl -s https://api.github.com/repos/madcowswe/ODrive/releases/latest | jq '.name' | get_version)" + #echo "previous version = $LAST_VERSION" + + NEW_VERSION="$(echo "${{ github.ref }}" | get_version)" + echo "version = $NEW_VERSION" + + ALL_TITLES="$(grep -E -n "^## .*[0-9]+\.[0-9]+\.[0-9]+" CHANGELOG.md)" + + echo "I found the following version headers in the changelog:" + echo "$ALL_TITLES" + + START_LINE="$(head -1 <<< "$ALL_TITLES" | cut -f 1 -d ':')" + END_LINE="$(head -2 <<< "$ALL_TITLES" | tail -1 | cut -f 1 -d ':')" + + BODY="$(head -$(("$END_LINE" - 1)) CHANGELOG.md | tail +$(("$START_LINE" + 1)))" + + IS_PRERELEASE="$(grep -F "$NEW_VERSION"-rc <<< "${{ github.ref }}" >/dev/null && echo true || echo false)" + echo "Is prerelease: $IS_PRERELEASE" + + # The git tag is later read when packaging the python tools and + # compiling the firmware. We set it explicitly here in case the + # workflow was triggered by something other than a git tag. + git tag "${{ github.ref }}" + + # Escape BODY string to set it as GitHub Actions output + BODY="${BODY//'%'/'%25'}" + BODY="${BODY//$'\n'/'%0A'}" + BODY="${BODY//$'\r'/'%0D'}" + + echo "::set-output name=body::$BODY" + echo "::set-output name=version::$NEW_VERSION" + echo "::set-output name=is_prerelease::$IS_PRERELEASE" + + - name: Create Release Draft + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Firmware version ${{ steps.release_specs.outputs.version }} + body: ${{ steps.release_specs.outputs.body }} + draft: true # Only do a draft so we have a change to manually review + # the release before it goes live. + prerelease: ${{ steps.release_specs.outputs.is_prerelease }} + + - name: Install Prerequisites + run: | + sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa + sudo add-apt-repository ppa:jonathonf/tup + sudo apt-get update + sudo apt-get install gcc-arm-embedded + sudo apt-get install tup + sudo apt install python3 python3-yaml python3-jinja2 python3-jsonschema + + - name: Build Python Tools + run: | + cd ${{ github.workspace }}/tools + python setup.py sdist + + cd dist + PKG_NAME="$(ls)" + if [ "$(wc -l <<< "$PKG_NAME")" != 1 ]; then + echo "Expected exactly one output file but got:" + ls + exit 1 + fi + + urls=( ${{ steps.create_release.outputs.upload_url }} ) + URL="${urls[0]}=python-${PKG_NAME}" + echo "uploading to $URL" + + curl --silent --show-error --fail \ + -H "Content-Type: application/octet-stream" \ + -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + --data-binary @"${PKG_NAME}" \ + "$URL" + + # Run the action under .github/actions/release-firmware for every board version + #- {uses: ./.github/actions/release-firmware, with: {board-version: v3.2, url: '${{ steps.create_release.outputs.upload_url }}', token: '${{ secrets.GITHUB_TOKEN }}' }} + #- {uses: ./.github/actions/release-firmware, with: {board-version: v3.3, url: '${{ steps.create_release.outputs.upload_url }}', token: '${{ secrets.GITHUB_TOKEN }}' }} + #- {uses: ./.github/actions/release-firmware, with: {board-version: v3.4-24V, url: '${{ steps.create_release.outputs.upload_url }}', token: '${{ secrets.GITHUB_TOKEN }}' }} + #- {uses: ./.github/actions/release-firmware, with: {board-version: v3.4-48V, url: '${{ steps.create_release.outputs.upload_url }}', token: '${{ secrets.GITHUB_TOKEN }}' }} + - {uses: ./.github/actions/release-firmware, with: {board_version: v3.5-24V, url: '${{ steps.create_release.outputs.upload_url }}', token: '${{ secrets.GITHUB_TOKEN }}' }} + - {uses: ./.github/actions/release-firmware, with: {board_version: v3.5-48V, url: '${{ steps.create_release.outputs.upload_url }}', token: '${{ secrets.GITHUB_TOKEN }}' }} + - {uses: ./.github/actions/release-firmware, with: {board_version: v3.6-24V, url: '${{ steps.create_release.outputs.upload_url }}', token: '${{ secrets.GITHUB_TOKEN }}' }} + - {uses: ./.github/actions/release-firmware, with: {board_version: v3.6-56V, url: '${{ steps.create_release.outputs.upload_url }}', token: '${{ secrets.GITHUB_TOKEN }}' }} diff --git a/docs/developer-guide.md b/docs/developer-guide.md index 7aee0287..fdeaf78d 100644 --- a/docs/developer-guide.md +++ b/docs/developer-guide.md @@ -276,9 +276,17 @@ We use GitHub Releases to provide firmware releases. 1. Cut off the changelog to reflect the new release 2. Merge the release candidate into master. -3. Push a (lightweight) tag to the master branch. Follow the existing naming convention. -4. Push the python tools to PyPI. -5. Edit the release on GitHub to add a title and description (copy&paste from changelog). +3. Push a (lightweight) tag to the master branch. + - Releases should be named like `fw-v0.1.23` + - Prereleases should be named like `fw-v0.1.23-rc` or `fw-v0.1.23-rc4` +4. Go to the GitHub Releases page and review the draft that will have been created by a GitHub Action by now. If you're satisfied, go ahead and publish it by pressing Edit > **Publish Release**. +5. Download `python-odrive-*.tar.gz` from the Releases page. The following step assumes that the version is `0.1.2` +6. Run + ``` + mv {python-,}odrive-0.1.23.tar.gz + pip3 install twine + python3 -m twine upload odrive-0.1.23.tar.gz # Only works once per version! + ``` ## Other code maintenance notes The cortex M4F processor has hardware single precision float unit. However double precision operations are not accelerated, and hence should be avoided. The following regex is helpful for cleaning out double constants: diff --git a/tools/odrive/version.py b/tools/odrive/version.py index 23b38b93..73bd8166 100644 --- a/tools/odrive/version.py +++ b/tools/odrive/version.py @@ -30,7 +30,7 @@ def get_version_from_git(): # Determine the current git commit version git_tag = subprocess.check_output(["git", "describe", "--always", "--tags", "--dirty=*"], cwd=script_dir) - git_tag = git_tag.decode(sys.stdout.encoding).rstrip('\n') + git_tag = git_tag.decode(sys.stdout.encoding or 'ascii').rstrip('\n') (major, minor, revision, is_prerelease) = version_str_to_tuple(git_tag)