From 0b3651cadbef457c54d1769d26c03e5abaaa9c32 Mon Sep 17 00:00:00 2001 From: Luke Tainton Date: Sun, 12 Jan 2025 19:53:06 +0100 Subject: [PATCH] chore(ci): move to reusable workflows (#333) Reviewed-on: https://git.tainton.uk/public/roboluke-tasks/pulls/333 --- .gitea/workflows-disabled/release.yml | 168 ++++++++++++++++++++++++ .gitea/workflows/ci.yml | 83 +++--------- .gitea/workflows/pr-title-semantic.yml | 20 --- .gitea/workflows/release.yml | 172 +++---------------------- 4 files changed, 199 insertions(+), 244 deletions(-) create mode 100644 .gitea/workflows-disabled/release.yml delete mode 100644 .gitea/workflows/pr-title-semantic.yml diff --git a/.gitea/workflows-disabled/release.yml b/.gitea/workflows-disabled/release.yml new file mode 100644 index 0000000..27181a4 --- /dev/null +++ b/.gitea/workflows-disabled/release.yml @@ -0,0 +1,168 @@ +name: Release +on: + schedule: + - cron: "0 9 * * 0" + issue_comment: + types: [created] + +jobs: + manual_trigger: + name: Manual Trigger Cleanup + runs-on: ubuntu-latest + if: ${{ gitea.event_name == 'issue_comment' }} + steps: + - name: Log event metadata + run: | + echo "Issue: ${{ gitea.event.issue.number }}" + echo "Comment: ${{ gitea.event.comment.body }}" + echo "User: ${{ gitea.event.comment.user.login }}" + + - name: Stop workflow if required conditions are not met + if: ${{ !contains(gitea.event.issue.number, '329') || !contains(gitea.event.comment.body, '/trigger-release') || !contains(gitea.event.comment.user.login, 'luke') }} + run: exit 1 + + - name: Delete issue comment + run: | + curl -X DELETE \ + -H "Authorization: token ${{ gitea.token }}" \ + "${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/issues/comments/${{ gitea.event.comment.id }}" + + # test: + # name: Unit Test + # uses: https://git.tainton.uk/public/roboluke-tasks/.gitea/workflows/ci.yml@main + # continue-on-error: true + + create_release: + name: Create Release + runs-on: ubuntu-latest + # needs: test + outputs: + release_name: ${{ steps.get_next_version.outputs.tag }} + steps: + - name: Check out repository + uses: actions/checkout@v4.2.2 + with: + fetch-depth: 0 + + - name: Changes since last tag + id: changes + run: | + rm -f .changes + git log $(git describe --tags --abbrev=0)..HEAD --no-merges --oneline >> .changes + cat .changes + + - name: Check for changes + run: | + if [[ -z $(grep '[^[:space:]]' .changes) ]] ; then + echo "changes=false" + echo "changes=false" >> "$GITEA_OUTPUT" + else + echo "changes=true" + echo "changes=true" >> "$GITEA_OUTPUT" + fi + + - name: Cancel if no changes + if: steps.changes.outputs.changes == 'false' + run: exit 1 + + - name: Set server URL + id: set_srvurl + run: | + SRVURL=$(echo "${{ gitea.server_url }}" | sed 's/https:\/\/\(.*\)/\1/') + echo "srvurl=$SRVURL" >> "$GITEA_OUTPUT" + + - name: Get next version + uses: TriPSs/conventional-changelog-action@v6 + id: get_next_version + with: + git-url: ${{ steps.set_srvurl.outputs.srvurl }} + github-token: ${{ gitea.token }} + preset: "conventionalcommits" + # preset: "angular" # This is the default + skip-commit: true + release-count: 1 + output-file: false + create-summary: true + skip-on-empty: true + skip-version-file: true + skip-tag: true + + - name: Create release + run: | + curl -s -X POST \ + -H "Authorization: token ${{ secrets.ACTIONS_TOKEN }}" \ + -H "accept: application/json" \ + -H "Content-Type: application/json" \ + -d "{\"tag_name\": \"${{ steps.get_next_version.outputs.tag }}\", \"name\": \"${{ steps.get_next_version.outputs.tag }}\", \"body\": \"${{ steps.get_next_version.outputs.changelog }}\"}" \ + "${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases" + + build_docker: + name: Build Docker Images + needs: create_release + steps: + - name: Update Docker configuration + continue-on-error: true + run: | + mkdir -p /etc/default + mkdir -p /etc/docker + touch -a /etc/default/docker + touch -a /etc/docker/daemon.json + echo "DOCKER_OPTS=\"--insecure-registry ${{ vars.PACKAGES_REGISTRY_URL }}\"" >> /etc/default/docker + echo "{\"insecure-registries\": [\"${{ vars.PACKAGES_REGISTRY_URL }}\"]}" > /etc/docker/daemon.json + + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ needs.create_release.outputs.release_name }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Gitea Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ vars.PACKAGES_REGISTRY_URL }} + username: ${{ vars.ACTIONS_USERNAME }} + password: ${{ secrets.ACTIONS_TOKEN }} + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ vars.GHCR_USERNAME }} + password: ${{ secrets.GHCR_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ghcr.io/${{ vars.GHCR_USERNAME }}/roboluke-tasks + ${{ vars.PACKAGES_REGISTRY_URL }}/${{ gitea.repository }} + tags: type=semver,pattern=v{{version}},value=${{ needs.create_release.outputs.release_name }} + + - name: Print metadata + run: | + printf "Annotations:\n${{ steps.meta.outputs.annotations }}" + echo "" + printf "Labels:\n${{ steps.meta.outputs.labels }}" + echo "" + printf "Tags:\n${{ steps.meta.outputs.tags }}" + + - name: Build images + uses: docker/build-push-action@v6 + with: + context: . + push: false + load: true + annotations: ${{ steps.meta.outputs.annotations }} + labels: ${{ steps.meta.outputs.labels }} + tags: ${{ steps.meta.outputs.tags }} + + - name: Push images + run: | + strtags="${{ steps.meta.outputs.tags }}" + readarray -t lines <<<"$strtags" + for element in "${lines[@]}"; do docker push "$element"; done + unset strtags lines diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index fc7057a..c78b2aa 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -1,73 +1,22 @@ name: CI on: pull_request: - types: [opened, synchronize, reopened] - paths-ignore: - - "README.md" - - "LICENSE.md" - - ".gitignore" - - "renovate.json" - - ".gitea/CODEOWNERS" - - ".archive" + types: + - opened + - edited + - synchronize + - reopened jobs: + validate_pr_title: + uses: https://git.tainton.uk/actions/gha-workflows/.gitea/workflows/conventional-commit.yml@main + with: + commit_message: ${{ gitea.event.pull_request.title }} + ci: - runs-on: ubuntu-latest - steps: - - name: Check out repository code - uses: actions/checkout@v4.2.2 - with: - fetch-depth: 0 - - - name: Run Hadolint - uses: hadolint/hadolint-action@v3.1.0 - with: - dockerfile: Dockerfile - output-file: hadolint.out - format: sonarqube - no-fail: true - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: "${{ vars.PYTHON_VERSION }}" - - - name: uv cache - uses: actions/cache@v4 - with: - path: /tmp/.uv-cache - key: uv-${{ runner.os }}-${{ hashFiles('uv.lock') }} - restore-keys: | - uv-${{ runner.os }}-${{ hashFiles('uv.lock') }} - uv-${{ runner.os }} - - - name: Install dependencies - run: uv sync - - - name: Lint - run: | - uv run pylint --fail-under=8 --recursive=yes --output-format=parseable --output=lintreport.txt app/ tests/ - cat lintreport.txt - - - name: Unit Test - run: | - uv run coverage run -m pytest -v --junitxml=testresults.xml - uv run coverage xml - sed -i 's@${{ gitea.workspace }}@/github/workspace@g' coverage.xml - - - name: Minimize uv cache - run: uv cache prune --ci - - - name: SonarQube Cloud Scan - uses: SonarSource/sonarqube-scan-action@v4.2.1 - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - - - name: Snyk Vulnerability Scan - uses: snyk/actions/python@master - continue-on-error: true # Sometimes vulns aren't immediately fixable - env: - SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - with: - command: snyk - args: test --all-projects + uses: https://git.tainton.uk/actions/gha-workflows/.gitea/workflows/ci-python-uv-with-docker.yml@main + with: + python-version: 3.13 + secrets: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} diff --git a/.gitea/workflows/pr-title-semantic.yml b/.gitea/workflows/pr-title-semantic.yml deleted file mode 100644 index 3730964..0000000 --- a/.gitea/workflows/pr-title-semantic.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: "Enforce Conventional Commit PR Title" - -on: - pull_request_target: - types: - - opened - - edited - - synchronize - - reopened - - labeled - - unlabeled - -jobs: - validate: - name: Validate PR Title - runs-on: ubuntu-latest - steps: - - uses: https://git.tainton.uk/actions/conventional-commits-check-action@v1.1.1 - with: - commit-message: ${{ gitea.event.pull_request.title }} diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 27181a4..1631474 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -1,168 +1,26 @@ name: Release on: + workflow_dispatch: schedule: - cron: "0 9 * * 0" - issue_comment: - types: [created] jobs: - manual_trigger: - name: Manual Trigger Cleanup - runs-on: ubuntu-latest - if: ${{ gitea.event_name == 'issue_comment' }} - steps: - - name: Log event metadata - run: | - echo "Issue: ${{ gitea.event.issue.number }}" - echo "Comment: ${{ gitea.event.comment.body }}" - echo "User: ${{ gitea.event.comment.user.login }}" - - - name: Stop workflow if required conditions are not met - if: ${{ !contains(gitea.event.issue.number, '329') || !contains(gitea.event.comment.body, '/trigger-release') || !contains(gitea.event.comment.user.login, 'luke') }} - run: exit 1 - - - name: Delete issue comment - run: | - curl -X DELETE \ - -H "Authorization: token ${{ gitea.token }}" \ - "${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/issues/comments/${{ gitea.event.comment.id }}" - - # test: - # name: Unit Test - # uses: https://git.tainton.uk/public/roboluke-tasks/.gitea/workflows/ci.yml@main - # continue-on-error: true + test: + uses: https://git.tainton.uk/actions/gha-workflows/.gitea/workflows/ci-python-uv-with-docker.yml@main + with: + python-version: 3.13 + secrets: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} create_release: name: Create Release - runs-on: ubuntu-latest - # needs: test - outputs: - release_name: ${{ steps.get_next_version.outputs.tag }} - steps: - - name: Check out repository - uses: actions/checkout@v4.2.2 - with: - fetch-depth: 0 + uses: https://git.tainton.uk/actions/gha-workflows/.gitea/workflows/create-release.yml@main - - name: Changes since last tag - id: changes - run: | - rm -f .changes - git log $(git describe --tags --abbrev=0)..HEAD --no-merges --oneline >> .changes - cat .changes - - - name: Check for changes - run: | - if [[ -z $(grep '[^[:space:]]' .changes) ]] ; then - echo "changes=false" - echo "changes=false" >> "$GITEA_OUTPUT" - else - echo "changes=true" - echo "changes=true" >> "$GITEA_OUTPUT" - fi - - - name: Cancel if no changes - if: steps.changes.outputs.changes == 'false' - run: exit 1 - - - name: Set server URL - id: set_srvurl - run: | - SRVURL=$(echo "${{ gitea.server_url }}" | sed 's/https:\/\/\(.*\)/\1/') - echo "srvurl=$SRVURL" >> "$GITEA_OUTPUT" - - - name: Get next version - uses: TriPSs/conventional-changelog-action@v6 - id: get_next_version - with: - git-url: ${{ steps.set_srvurl.outputs.srvurl }} - github-token: ${{ gitea.token }} - preset: "conventionalcommits" - # preset: "angular" # This is the default - skip-commit: true - release-count: 1 - output-file: false - create-summary: true - skip-on-empty: true - skip-version-file: true - skip-tag: true - - - name: Create release - run: | - curl -s -X POST \ - -H "Authorization: token ${{ secrets.ACTIONS_TOKEN }}" \ - -H "accept: application/json" \ - -H "Content-Type: application/json" \ - -d "{\"tag_name\": \"${{ steps.get_next_version.outputs.tag }}\", \"name\": \"${{ steps.get_next_version.outputs.tag }}\", \"body\": \"${{ steps.get_next_version.outputs.changelog }}\"}" \ - "${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases" - - build_docker: - name: Build Docker Images + create_docker: + name: Create Docker Image needs: create_release - steps: - - name: Update Docker configuration - continue-on-error: true - run: | - mkdir -p /etc/default - mkdir -p /etc/docker - touch -a /etc/default/docker - touch -a /etc/docker/daemon.json - echo "DOCKER_OPTS=\"--insecure-registry ${{ vars.PACKAGES_REGISTRY_URL }}\"" >> /etc/default/docker - echo "{\"insecure-registries\": [\"${{ vars.PACKAGES_REGISTRY_URL }}\"]}" > /etc/docker/daemon.json - - - name: Check out repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ needs.create_release.outputs.release_name }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to Gitea Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ vars.PACKAGES_REGISTRY_URL }} - username: ${{ vars.ACTIONS_USERNAME }} - password: ${{ secrets.ACTIONS_TOKEN }} - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ vars.GHCR_USERNAME }} - password: ${{ secrets.GHCR_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: | - ghcr.io/${{ vars.GHCR_USERNAME }}/roboluke-tasks - ${{ vars.PACKAGES_REGISTRY_URL }}/${{ gitea.repository }} - tags: type=semver,pattern=v{{version}},value=${{ needs.create_release.outputs.release_name }} - - - name: Print metadata - run: | - printf "Annotations:\n${{ steps.meta.outputs.annotations }}" - echo "" - printf "Labels:\n${{ steps.meta.outputs.labels }}" - echo "" - printf "Tags:\n${{ steps.meta.outputs.tags }}" - - - name: Build images - uses: docker/build-push-action@v6 - with: - context: . - push: false - load: true - annotations: ${{ steps.meta.outputs.annotations }} - labels: ${{ steps.meta.outputs.labels }} - tags: ${{ steps.meta.outputs.tags }} - - - name: Push images - run: | - strtags="${{ steps.meta.outputs.tags }}" - readarray -t lines <<<"$strtags" - for element in "${lines[@]}"; do docker push "$element"; done - unset strtags lines + if: ${{ needs.create_release.outputs.success == 'true' }} + uses: https://git.tainton.uk/actions/gha-workflows/.gitea/workflows/build-push-docker.yml@main + with: + release: ${{ needs.create_release.outputs.release_name }}