diff --git a/.github/.release-please-manifest.json b/.github/.release-please-manifest.json new file mode 100644 index 00000000..37fcefaa --- /dev/null +++ b/.github/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "1.0.0" +} diff --git a/.github/release-please-config.json b/.github/release-please-config.json new file mode 100644 index 00000000..bb93a2bc --- /dev/null +++ b/.github/release-please-config.json @@ -0,0 +1,10 @@ +{ + "packages": { + ".": { + "release-type": "ruby", + "package-name": "html2rss-web", + "version-file": "config/version.rb", + "changelog-path": "CHANGELOG.md" + } + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3afaea33..f270f990 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -159,91 +159,3 @@ jobs: DOCKER_SMOKE_SKIP_BUILD: "true" SMOKE_AUTO_SOURCE_ENABLED: ${{ matrix.smoke_auto_source_enabled }} run: bundle exec rake - - docker-publish: - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - needs: - - docker-test - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - env: - IMAGE_NAME: html2rss/web - TAG_SHA: ${{ github.sha }} - steps: - - name: Checkout code - uses: actions/checkout@v6 - - - name: Setup Node.js for Docker build - uses: actions/setup-node@v6 - with: - node-version-file: ".tool-versions" - cache: npm - cache-dependency-path: frontend/package-lock.json - - - name: Install frontend dependencies - run: npm ci - working-directory: frontend - - - name: Build frontend static assets - run: npm run build - working-directory: frontend - - - name: Set up QEMU - uses: docker/setup-qemu-action@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 - - - name: Get Git commit timestamps - run: echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV - - - name: Extract metadata - id: meta - uses: docker/metadata-action@v6 - with: - images: ${{ env.IMAGE_NAME }} - - - name: Log in to DockerHub - uses: docker/login-action@v4 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Cache Docker layers - uses: actions/cache@v5 - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildx- - - - name: Build and push Docker image - uses: docker/build-push-action@v7 - env: - SOURCE_DATE_EPOCH: ${{ env.TIMESTAMP }} - with: - context: . - push: true - tags: | - html2rss/web:latest - html2rss/web:${{ github.sha }} - ${{ steps.meta.outputs.tags }} - platforms: linux/amd64,linux/arm64 - cache-from: type=local,src=/tmp/.buildx-cache - cache-to: type=local,dest=/tmp/.buildx-cache-new - provenance: true - sbom: true - labels: | - org.opencontainers.image.source=https://github.com/${{ github.repository }} - org.opencontainers.image.created=${{ github.event.head_commit.timestamp }} - org.opencontainers.image.revision=${{ github.sha }} - org.opencontainers.image.title=html2rss-web - org.opencontainers.image.description=Generates RSS feeds of any website & serves to the web! - org.opencontainers.image.sbom=https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts - - - name: Move updated cache into place - run: | - rm -rf /tmp/.buildx-cache - mv /tmp/.buildx-cache-new /tmp/.buildx-cache diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..c8726731 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,131 @@ +name: release + +on: + push: + branches: + - main + +permissions: + contents: write + pull-requests: write + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: true + +jobs: + release: + runs-on: ubuntu-latest + outputs: + release_created: ${{ steps.release.outputs.release_created }} + tag_name: ${{ steps.release.outputs.tag_name }} + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Run release-please + id: release + uses: googleapis/release-please-action@v4 + with: + token: ${{ secrets.RELEASE_PLEASE_TOKEN || github.token }} + config-file: .github/release-please-config.json + manifest-file: .github/.release-please-manifest.json + + docker-publish: + if: needs.release.outputs.release_created == 'true' + needs: + - release + runs-on: ubuntu-latest + env: + IMAGE_NAME: html2rss/web + TAG_SHA: ${{ github.sha }} + RELEASE_TAG: ${{ needs.release.outputs.tag_name }} + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Setup Node.js for Docker build + uses: actions/setup-node@v6 + with: + node-version-file: ".tool-versions" + cache: npm + cache-dependency-path: frontend/package-lock.json + + - name: Install frontend dependencies + run: npm ci + working-directory: frontend + + - name: Build frontend static assets + run: npm run build + working-directory: frontend + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Get Git commit timestamp + run: echo "TIMESTAMP=$(git log -1 --format=%cI)" >> "$GITHUB_ENV" + + - name: Compute Docker tags + id: tags + run: | + release_version="${RELEASE_TAG#v}" + echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV" + major="${release_version%%.*}" + { + echo "tags<> "$GITHUB_OUTPUT" + + - name: Log in to DockerHub + uses: docker/login-action@v4 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Cache Docker layers + uses: actions/cache@v4 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Build and push Docker image + uses: docker/build-push-action@v7 + env: + SOURCE_DATE_EPOCH: ${{ env.TIMESTAMP }} + with: + context: . + push: true + tags: ${{ steps.tags.outputs.tags }} + build-args: | + BUILD_TAG=${{ env.RELEASE_VERSION }} + GIT_SHA=${{ github.sha }} + platforms: linux/amd64,linux/arm64 + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new + provenance: true + sbom: true + labels: | + org.opencontainers.image.created=${{ env.TIMESTAMP }} + org.opencontainers.image.description=Generates RSS feeds of any website & serves to the web! + org.opencontainers.image.ref.name=${{ env.RELEASE_TAG }} + org.opencontainers.image.revision=${{ github.sha }} + org.opencontainers.image.source=https://github.com/${{ github.repository }} + org.opencontainers.image.title=html2rss-web + org.opencontainers.image.url=https://github.com/${{ github.repository }}/releases/tag/${{ env.RELEASE_TAG }} + org.opencontainers.image.version=${{ env.RELEASE_VERSION }} + + - name: Move updated cache into place + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache diff --git a/.github/workflows/release_artifacts.yml b/.github/workflows/release_artifacts.yml new file mode 100644 index 00000000..aa63ab2f --- /dev/null +++ b/.github/workflows/release_artifacts.yml @@ -0,0 +1,61 @@ +name: refresh release artifacts + +on: + pull_request_target: + types: + - opened + - reopened + - synchronize + branches: + - main + +permissions: + contents: write + pull-requests: write + +concurrency: + group: release-artifacts-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + refresh-generated-artifacts: + if: github.event.pull_request.head.repo.full_name == github.repository && startsWith(github.event.pull_request.head.ref, 'release-please--branches--') + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.ref }} + fetch-depth: 0 + + - uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + + - uses: actions/setup-node@v6 + with: + node-version-file: ".tool-versions" + cache: npm + cache-dependency-path: frontend/package-lock.json + + - name: Install frontend dependencies + run: npm ci + working-directory: frontend + + - name: Refresh OpenAPI artifacts + run: | + make openapi + make openapi-client + + - name: Commit generated artifacts + run: | + if git diff --quiet -- public/openapi.yaml frontend/src/api/generated; then + echo "Generated artifacts already up to date" + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add public/openapi.yaml frontend/src/api/generated + git commit -m "chore: refresh release artifacts" + git push diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..6361e43e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# Changelog + +All notable changes to this project will be documented in this file. diff --git a/Dockerfile b/Dockerfile index 63f7aaf9..aaef3117 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,9 +45,14 @@ LABEL maintainer="Gil Desmarais " SHELL ["/bin/ash", "-o", "pipefail", "-c"] +ARG BUILD_TAG=unknown +ARG GIT_SHA=unknown + ENV PORT=4000 \ RACK_ENV=production \ - RUBY_YJIT_ENABLE=1 + RUBY_YJIT_ENABLE=1 \ + BUILD_TAG=${BUILD_TAG} \ + GIT_SHA=${GIT_SHA} EXPOSE $PORT diff --git a/config/version.rb b/config/version.rb new file mode 100644 index 00000000..8b39128e --- /dev/null +++ b/config/version.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Html2rss + module Web + VERSION = '1.0.0' + public_constant :VERSION + end +end diff --git a/spec/html2rss/web_spec.rb b/spec/html2rss/web_spec.rb new file mode 100644 index 00000000..64a08ba7 --- /dev/null +++ b/spec/html2rss/web_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require_relative '../../config/version' + +RSpec.describe Html2rss::Web do + describe 'VERSION' do + it 'defines the canonical application release version' do + expect(described_class::VERSION).to eq('1.0.0') + end + end +end diff --git a/spec/support/openapi.rb b/spec/support/openapi.rb index 5705a7d5..68d8b5a0 100644 --- a/spec/support/openapi.rb +++ b/spec/support/openapi.rb @@ -3,10 +3,11 @@ return unless ENV['OPENAPI'] require 'rspec/openapi' +require_relative '../../config/version' RSpec::OpenAPI.path = 'public/openapi.yaml' RSpec::OpenAPI.title = 'html2rss-web API' -RSpec::OpenAPI.application_version = '1.0.0' +RSpec::OpenAPI.application_version = Html2rss::Web::VERSION RSpec::OpenAPI.enable_example = false RSpec::OpenAPI.enable_example_summary = false RSpec::OpenAPI.example_types = [:request]