diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..8b003dc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +# yaml-language-server: $schema=https://json.schemastore.org/dependabot-2.0.json +version: 2 + +updates: + + - package-ecosystem: 'pip' + directory: '/' + schedule: + interval: 'weekly' + + - package-ecosystem: 'docker' + directory: '/' + schedule: + interval: 'weekly' + + - package-ecosystem: 'github-actions' + directory: '/' + schedule: + interval: 'weekly' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..409b78a --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,39 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: 'CodeQL analysis' + +on: + push: + tags: ['*'] + branches: ['*'] + pull_request: + branches: ['*'] + schedule: + - cron: '25 10 * * 3' + workflow_dispatch: + +permissions: {} + +jobs: + + analyze: + name: 'CodeQL analysis' + runs-on: 'ubuntu-latest' + permissions: + actions: 'read' + contents: 'read' + security-events: 'write' + strategy: + fail-fast: false + matrix: + language: ['python'] + steps: + - name: 'Checkout' + uses: 'actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11' + - name: 'Initialize CodeQL' + uses: 'github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a' + with: + languages: '${{ matrix.language }}' + - name: 'Autobuild' + uses: 'github/codeql-action/autobuild@cdcdbb579706841c47f7063dda365e292e5cad7a' + - name: 'Perform CodeQL Analysis' + uses: 'github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a' diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..9698b54 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,112 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json +name: 'Main' + +on: + push: + tags: ['*'] + branches: ['*'] + pull_request: + branches: ['*'] + schedule: + - cron: '25 12 * * 3' + workflow_dispatch: + +permissions: {} + +jobs: + + build: + name: 'Build on Python ${{ matrix.python_version }} on ${{ matrix.os }}' + runs-on: '${{ matrix.os }}' + permissions: + contents: 'read' + strategy: + fail-fast: false + matrix: + python_version: ['3.x', '3.10', 'pypy3.10'] + os: ['ubuntu-latest', 'windows-latest', 'macos-latest'] + steps: + - name: 'Checkout' + uses: 'actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11' + - name: 'Use Python ${{ matrix.python_version }}' + uses: 'actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c' + with: + python-version: '${{ matrix.python_version }}' + check-latest: true + cache: 'pip' + cache-dependency-path: | + **/requirements.txt + **/requirements-dev.txt + - name: 'Build' + run: 'make all' + - name: 'Verify that the working tree is clean' + shell: 'bash' + run: '[ -z "$(git status --porcelain ./)" ] || { git diff >&2; exit 1; }' + + build-push-docker: + name: 'Build and push Docker images' + needs: ['build'] + runs-on: 'ubuntu-latest' + permissions: + contents: 'read' + packages: 'write' + steps: + - name: 'Checkout' + uses: 'actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11' + - name: 'Set up QEMU' + uses: 'docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3' + - name: 'Set up Docker Buildx' + uses: 'docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226' + - name: 'Login to GitHub Container Registry' + if: "github.event_name != 'pull_request'" + uses: 'docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d' + with: + registry: 'ghcr.io' + username: '${{ github.actor }}' + password: '${{ secrets.GITHUB_TOKEN }}' + - name: 'Extract metadata' + id: 'meta' + uses: 'docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81' + with: + images: | + ghcr.io/${{ github.repository }} + tags: | + type=ref,event=branch + type=semver,pattern=v{{version}} + type=semver,pattern=v{{major}}.{{minor}} + type=semver,pattern=v{{major}} + - name: 'Build and push' + uses: 'docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56' + with: + context: './' + platforms: 'linux/amd64,linux/arm64' + tags: '${{ steps.meta.outputs.tags }}' + labels: '${{ steps.meta.outputs.labels }}' + push: "${{ github.event_name != 'pull_request' }}" + + publish-github-release: + name: 'Publish GitHub release' + if: "startsWith(github.ref, 'refs/tags/v')" + needs: ['build', 'build-push-docker'] + runs-on: 'ubuntu-latest' + permissions: + contents: 'write' + steps: + - name: 'Publish' + env: + GITHUB_PAT: '${{ secrets.GITHUB_TOKEN }}' + run: | + RELEASE_STATUS="$(curl -fs --proto '=https' --tlsv1.3 --globoff \ + --url "https://api.github.com/repos/${GITHUB_REPOSITORY:?}/releases/tags/${GITHUB_REF_NAME:?}" \ + --header "Authorization: Bearer ${GITHUB_PAT:?}" \ + --header 'Accept: application/vnd.github.v3+json' \ + --header 'Content-Type: application/json' \ + --write-out '%{http_code}' --output /dev/null ||:)" + if [ "${RELEASE_STATUS:?}" = '200' ]; then exit 0; fi + RELEASE_ID="$(curl -fsS --proto '=https' --tlsv1.3 --globoff \ + --url "https://api.github.com/repos/${GITHUB_REPOSITORY:?}/releases" \ + --header "Authorization: Bearer ${GITHUB_PAT:?}" \ + --header 'Accept: application/vnd.github.v3+json' \ + --header 'Content-Type: application/json' \ + --data "$(jq -rn --arg tag "${GITHUB_REF_NAME:?}" '{"name": $tag, "tag_name": $tag, "generate_release_notes": true}')" | jq -r '.id')" + if [ -z "${RELEASE_ID-}" ] || [ "${RELEASE_ID:?}" = 'null' ]; then exit 1; fi