diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..eae2d1c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,70 @@ +name: Release + +on: + push: + tags: + - '*' + +jobs: + release: + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: ubuntu-latest + locale: C.UTF-8 + - os: macos-10.15 + locale: en_US.UTF-8 + env: + LC_ALL: ${{ matrix.locale }} + LANG: ${{ matrix.locale }} + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.6 + - name: Upgrade pip + run: python -m pip install --upgrade pip setuptools==44.0.0 + - name: Print info about the current python installation + run: make ci-info + - name: Install requirements + run: make bootstrap-dev-plugins + - name: Run tests + run: make tests + - name: Create bundle + run: make bundle + - name: Test bundle + run: make ci-test-bundle + - name: Get release description + id: release-description + # We must escape multi-line string, as per: + # https://medium.com/agorapulse-stories/how-to-work-with-multiline-string-variables-in-github-actions-23f56447d209 + run: | + make release-description + cat release_description.md + description="$(cat release_description.md)" + description="${description//'%'/'%25'}" + description="${description//$'\n'/'%0A'}" + description="${description//$'\r'/'%0D'}" + echo "::set-output name=text::$description" + shell: bash + - name: Get release file name + id: release-file + run: echo "::set-output name=filename::tutor-$(uname -s)_$(uname -m)" + shell: bash + - name: Debug release variables + run: | + echo "Publish file '${{ steps.release-file.outputs.filename }}' to release ${{ github.ref }}" + echo "================" + echo "${{ steps.release-description.outputs.text }}" + - name: Upload bundle + # https://github.com/marketplace/actions/upload-files-to-a-github-release + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./dist/tutor + asset_name: "${{ steps.release-file.outputs.filename }}" + tag: ${{ github.ref }} + overwrite: true + body: "${{ steps.release-description.outputs.text }}" diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml new file mode 100644 index 0000000..21aef85 --- /dev/null +++ b/.github/workflows/sync.yml @@ -0,0 +1,15 @@ +name: Sync with private repo + +on: + push: + branches: [ master ] + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Add remote + run: git remote add overhangio https://${{ secrets.GIT_USERNAME }}:${{ secrets.GIT_PASSWORD }}@git.overhang.io/core/tutor.git + - name: Push + run: git push overhangio master diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..d0b2349 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,31 @@ +name: Run tests + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Upgrade pip + run: python -m pip install --upgrade pip setuptools==44.0.0 + - name: Install dependencies + run: pip install -r requirements/dev.txt + - name: Static code analysis + run: make test-lint + - name: Python unit tests + run: make test-unit + - name: Static type checks + run: make test-types + - name: Code formatting + run: make test-format + - name: Package tests + run: make test-pythonpackage diff --git a/.gitignore b/.gitignore index 4a24ea2..8e55cc4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ __pycache__ /build/ /dist/ -/releases/ +/release_description.md diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 932deff..0000000 --- a/.travis.yml +++ /dev/null @@ -1,32 +0,0 @@ -language: python -matrix: - include: - - os: linux - # We need an older version of python in order to have compatibility with - # older versions of libc - dist: xenial - python: 3.6 - - os: osx - language: generic - -env: - jobs: - - PATH=/tmp/bin/:$PATH - -script: - # In Mac OS python and pip binaries are v2, so we create symlinks - - mkdir /tmp/bin && ln -s $(which python3) /tmp/bin/python && ln -s $(which pip3) /tmp/bin/pip - - pip install --upgrade pip setuptools==44.0.0 - - make ci-info - - make bootstrap-dev-plugins - - make test - - make bundle - - make ci-test-bundle - -deploy: - # Create github release and push binary to github - - provider: script - script: make ci-push-bundle - skip_cleanup: true - on: - tags: true \ No newline at end of file diff --git a/Makefile b/Makefile index 28a36e5..ce9e941 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,8 @@ bootstrap-dev-plugins: bootstrap-dev ## Install dev requirement and all supporte bundle: ## Bundle the tutor package in a single "dist/tutor" executable pyinstaller tutor.spec -release: test ## Create a release tag and push it to origin +release: test release-unsafe ## Create a release tag and push it to origin +release-unsafe: $(MAKE) release-tag release-push TAG=v$(shell make version) release-tag: @echo "=== Creating tag $(TAG)" @@ -68,6 +69,10 @@ release-push: git push origin :$(TAG) || true git push origin $(TAG) +release-description: ## Write the current release description to a file + sed "s/TUTOR_VERSION/v$(shell make version)/g" docs/_release_description.md > release_description.md + git log -1 --pretty=format:%b >> release_description.md + ###### Continuous integration tasks pull-base-images: # Manually pull base images @@ -90,30 +95,6 @@ ci-test-bundle: ## Run basic tests on bundle ./dist/tutor plugins list ./dist/tutor license --help -./releases/github-release: ## Download github-release binary - mkdir -p releases/ - cd releases/ \ - && curl -sSL -o ./github-release.bz2 "https://github.com/github-release/github-release/releases/download/v0.10.0/$(shell uname -s | tr "[:upper:]" "[:lower:]")-amd64-github-release.bz2" \ - && bzip2 -d -f ./github-release.bz2 \ - && chmod a+x ./github-release - -ci-push-bundle: ./releases/github-release ## Upload assets to github - sed "s/TUTOR_VERSION/v$(shell make version)/g" docs/_release_description.md > releases/description.md - git log -1 --pretty=format:%b >> releases/description.md - ./releases/github-release release \ - --user overhangio \ - --repo tutor \ - --tag "v$(shell make version)" \ - --name "v$(shell make version)" \ - --description "$$(cat releases/description.md)" || true - ./releases/github-release upload \ - --user overhangio \ - --repo tutor \ - --tag "v$(shell make version)" \ - --name "tutor-$$(uname -s)_$$(uname -m)" \ - --file ./dist/tutor \ - --replace - ci-bootstrap-images: pip install . tutor config save diff --git a/README.rst b/README.rst index 5b64a1e..3aaf949 100644 --- a/README.rst +++ b/README.rst @@ -12,9 +12,9 @@ Tutor: the docker-based Open edX distribution designed for peace of mind | -.. image:: https://img.shields.io/travis/overhangio/tutor.svg?label=Release%20build&style=flat-square +.. image:: https://github.com/overhangio/tutor/actions/workflows/release.yml/badge.svg :alt: Release build status - :target: https://travis-ci.org/overhangio/tutor + :target: https://github.com/overhangio/tutor/actions/workflows/release.yml .. image:: https://img.shields.io/badge/docs-current-blue.svg?style=flat-square :alt: Documentation @@ -82,4 +82,4 @@ Contributing We welcome contributions to Tutor! To learn how you can contribute, please check the relevant section of the Tutor docs: `https://docs.tutor.overhang.io/tutor.html#contributing `__. -.. _readme_contributing_end: \ No newline at end of file +.. _readme_contributing_end: diff --git a/github-release.py b/github-release.py deleted file mode 100755 index 0f4585d..0000000 --- a/github-release.py +++ /dev/null @@ -1,83 +0,0 @@ -#! /usr/bin/env python3 - -""" -This is a quick-and-dirty script to upload release assets to Github via the API. -Currently, we do not use this script and rely instead on the github-release binary. This -is just in case the github-release binary stops working (as it has before). - -Run script with: - - GITHUB_TOKEN=s3cr3t ./github-release.py v3.00.00 ./dist/tutor-openedx-3.00.00.tar.gz "tutor-$(uname -s)_$(uname -m)" -""" - -import argparse -import os -from urllib.parse import urlencode - -import requests - -HEADERS = { - "Accept": "application/vnd.github.v3+json", - "Authorization": "token {}".format(os.environ["GITHUB_TOKEN"]), -} -RELEASES_URL = "https://api.github.com/repos/overhangio/tutor/releases" - - -def main(): - parser = argparse.ArgumentParser( - description="Create github releases and upload assets" - ) - parser.add_argument("tag") - parser.add_argument("asset") - parser.add_argument("asset_name") - args = parser.parse_args() - release = get_or_create_release(args.tag) - overwrite_asset(args.asset, args.asset_name, release) - - -def get_or_create_release(tag): - # https://developer.github.com/v3/repos/releases/#get-a-release-by-tag-name - url = "{}/tags/{}".format(RELEASES_URL, tag) - response = requests.get(url) - if response.status_code == 200: - print("Release {} already exists".format(tag)) - return response.json() - - print("Creating release {}".format(tag)) - description = open( - os.path.join(os.path.dirname("__file__"), "docs", "_release_description.md") - ).read() - params = {"tag_name": tag, "name": tag, "body": description} - # https://developer.github.com/v3/repos/releases/#create-a-release - return requests.post(RELEASES_URL, json=params, headers=HEADERS,).json() - - -def overwrite_asset(asset, asset_name, release): - # https://developer.github.com/v3/repos/releases/#list-assets-for-a-release - url = "{}/{}/assets".format(RELEASES_URL, release["id"]) - for existing_asset in requests.get(url).json(): - if existing_asset["name"] == asset_name: - print("Deleting existing asset") - # https://developer.github.com/v3/repos/releases/#delete-a-release-asset - delete_url = "{}/assets/{}".format(RELEASES_URL, existing_asset["id"]) - response = requests.delete(delete_url, headers=HEADERS) - if response.status_code != 204: - print(response, response.content) - raise ValueError("Could not delete asset") - print("Uploading asset") - upload_asset(asset, asset_name, release) - - -def upload_asset(asset, asset_name, release): - upload_url = release["upload_url"].replace( - "{?name,label}", "?" + urlencode({"name": asset_name}) - ) - files = {"file": (asset_name, open(asset, "rb"))} - response = requests.post(upload_url, files=files, headers=HEADERS) - if response.status_code > 299: - print(response, response.content) - raise ValueError("Could not upload asset to release") - - -if __name__ == "__main__": - main()