diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..775bcd1 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +HERALD_SWIFTTEST_OS_AUTH=$TF_VAR_OS_PASSWORD +HERALD_SWIFTTEST_OS_PASSWORD=$TF_VAR_OS_PASSWORD +HERALD_SWIFTTEST_OS_PROJECT_NAME=$TF_VAR_OS_PROJECT_NAME +HERALD_SWIFTTEST_OS_USERNAME=$TF_VAR_OS_USERNAME +HEARLD_SWIFTTEST_AUTH_URL=https://api.pub1.infomaniak.cloud/identity/v3 +HEARLD_SWIFTTEST_OS_REGION_NAME=dc3-a diff --git a/.github/workflows/build-image.yml b/.github/workflows/build-image.yml index 1d15299..c5c85ee 100644 --- a/.github/workflows/build-image.yml +++ b/.github/workflows/build-image.yml @@ -5,16 +5,16 @@ on: branches: - main paths: - - 'src/**' - - 'tools/**' - - '.github/workflows/build-image.yml' + - "src/**" + - "tools/**" + - ".github/workflows/build-image.yml" pull_request: branches: - main paths: - - 'src/**' - - 'tools/**' - - '.github/workflows/build-image.yml' + - "src/**" + - "tools/**" + - ".github/workflows/build-image.yml" workflow_dispatch: env: diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml new file mode 100644 index 0000000..13c559e --- /dev/null +++ b/.github/workflows/checks.yml @@ -0,0 +1,94 @@ +name: checks + +on: + push: + branches: + - main + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + workflow_dispatch: + +env: + DOCKER_CMD: docker + UV_CACHE_DIR: /tmp/.uv-cache + +jobs: + checks: + runs-on: ubuntu-latest + env: + HERALD_SWIFTTEST_OS_USERNAME: ${{ secrets.OPENSTACK_USERNAME }} + HERALD_SWIFTTEST_OS_PASSWORD: ${{ secrets.OPENSTACK_PASSWORD }} + HERALD_SWIFTTEST_OS_PROJECT_NAME: ${{ secrets.OPENSTACK_PROJECT }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@v16 + + - name: Set up Nix cache + uses: DeterminateSystems/magic-nix-cache-action@v9 + + - name: Run pre-commit hooks via prek + run: nix develop --command prek run --all-files + + - name: Cache Deno + uses: actions/cache@v4 + with: + path: ~/.cache/deno + key: ${{ runner.os }}-deno-${{ hashFiles('deno.lock') }} + restore-keys: | + ${{ runner.os }}-deno- + + - name: Restore uv cache + uses: actions/cache@v5 + with: + path: /tmp/.uv-cache + key: uv-${{ runner.os }}-${{ hashFiles('s3-tests/requirements.txt') }} + restore-keys: | + uv-${{ runner.os }}-${{ hashFiles('s3-tests/requirements.txt') }} + uv-${{ runner.os }} + + - name: Start services + run: nix develop --command deno run --allow-all x/compose-up.ts s3 db + + - name: Wait for MinIO + run: | + for i in {1..30}; do + if curl -f http://localhost:9000/minio/health/live; then + echo "MinIO is ready" + exit 0 + fi + echo "Waiting for MinIO..." + sleep 2 + done + echo "MinIO failed to start" + exit 1 + + - name: Integration tests + run: nix develop --command deno task test + + - name: S3 Compatibility (MinIO) + run: nix develop --command deno run --allow-all x/s3-tests.ts --backend minio + + - name: S3 Compatibility (Swift) + if: env.HERALD_SWIFTTEST_OS_USERNAME != '' + env: + HERALD_SWIFTTEST_OS_REGION_NAME: dc3-a + HERALD_SWIFTTEST_AUTH_URL: https://api.pub1.infomaniak.cloud/identity/v3 + run: nix develop --command deno run --allow-all x/s3-tests.ts --backend swift + + - name: Minimize uv cache + run: nix develop --command uv cache prune --ci + + - name: Dump logs on failure + if: failure() + run: | + echo "--- s3-tests/s3-tests.log ---" + cat s3-tests/s3-tests.log || true + echo "--- s3-tests/herald-proxy.log ---" + cat s3-tests/herald-proxy.log || true + echo "--- s3-tests/herald-proxy-swift.log ---" + cat s3-tests/herald-proxy-swift.log || true diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml deleted file mode 100644 index c878fb7..0000000 --- a/.github/workflows/pre-commit.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: pre-commit - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - workflow_dispatch: - -jobs: - pre-commit: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install Nix - uses: DeterminateSystems/nix-installer-action@main - - - name: Set up Nix cache - uses: DeterminateSystems/magic-nix-cache-action@main - - - name: Run pre-commit hooks via prek - run: nix develop --command prek run --all-files diff --git a/.github/workflows/release-request.yml b/.github/workflows/release-request.yml deleted file mode 100644 index e11d30b..0000000 --- a/.github/workflows/release-request.yml +++ /dev/null @@ -1,159 +0,0 @@ -name: Prepare Release - -on: - workflow_dispatch: - push: - branches: - - main - -jobs: - check-version: - name: Check Commitizen Version - runs-on: ubuntu-latest - outputs: - version: ${{ steps.version.outputs.version }} - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Configure Git - run: | - git config user.name "${{ github.actor }}" - git config user.email "${{ github.actor }}@users.noreply.github.com" - - - name: Get current version (without bumping or pushing) - id: version - uses: commitizen-tools/commitizen-action@master - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - push: false - dry_run: true - changelog: false - - prepare-release-pr: - name: Create Release Branch and PR - needs: check-version - if: ${{ needs.check-version.outputs.version != '' && github.ref == 'refs/heads/main' }} - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - ref: main - - - name: Bump version using Commitizen - id: cz - uses: commitizen-tools/commitizen-action@master - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - git_name: ${{ github.actor }} - git_email: ${{ github.actor }}@users.noreply.github.com - push: false - changelog: true - dry_run: false - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v8 - with: - title: "Release ${{ steps.cz.outputs.version }}" - body: "Automated PR for version bump to ${{ steps.cz.outputs.version }}" - branch: "release-v${{ steps.cz.outputs.version }}" - delete-branch: true - - check-release: - runs-on: ubuntu-latest - # if: github.ref == 'refs/heads/main' && github.event_name == 'push' - outputs: - release: ${{ steps.check.outputs.release }} - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - ref: main - - - name: Configure Git - run: | - git config user.name "${{ github.actor }}" - git config user.email "${{ github.actor }}@users.noreply.github.com" - - - name: Get current version - id: version - run: | - VERSION=$(yq '.commitizen.version' .cz.yaml) - echo "version=$VERSION" >> $GITHUB_OUTPUT - - - name: Check if GitHub release already exists - id: check - run: | - VERSION=${{ steps.version.outputs.version }} - echo "Detected version: $VERSION" - - RELEASE_EXISTS=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ - https://api.github.com/repos/${{ github.repository }}/releases/tags/v$VERSION \ - | jq -r '.tag_name // empty') - - if [[ "$RELEASE_EXISTS" == "v$VERSION" ]]; then - echo "Release v$VERSION already exists." - echo "release=" >> $GITHUB_OUTPUT - else - echo "Release v$VERSION does not exist yet." - echo "release=$VERSION" >> $GITHUB_OUTPUT - fi - finalize-release: - name: Finalize Release - needs: check-release - if: ${{ needs.check-release.outputs.release != '' }} - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Tag and Push - run: | - git config user.name "${{ github.actor }}" - git config user.email "${{ github.actor }}@users.noreply.github.com" - git tag -a "v${{ needs.check-release.outputs.release }}" -m "Release v${{ needs.check-release.outputs.release }}" - git push origin "v${{ needs.check-release.outputs.release }}" - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - tag_name: "v${{ needs.check-release.outputs.release }}" - name: "Release v${{ needs.check-release.outputs.release }}" - body_path: "CHANGELOG.md" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - build-docker: - name: Build and Push Docker - needs: check-release - if: ${{ needs.check-release.outputs.release != '' }} - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - - - uses: docker/setup-buildx-action@v3 - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and Push Docker - uses: docker/build-push-action@v6 - with: - context: . - file: ./Dockerfile - platforms: linux/amd64,linux/arm64 - push: true - tags: ghcr.io/${{ github.repository_owner }}/herald:v${{ needs.check-release.outputs.release }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index f09fed5..0000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,119 +0,0 @@ -name: test suite -run-name: test suite for ${{ github.event.pull_request.title || github.ref }} -on: - workflow_dispatch: - push: - branches: - - main - pull_request: - types: - - opened - - reopened - - synchronize - - ready_for_review - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -env: - DENO_V: 2.3.5 - GHJK_VERSION: "v0.3.2" - GHJK_ENV: "ci" - -jobs: - changes: - runs-on: ubuntu-latest - permissions: - pull-requests: read - steps: - - uses: actions/checkout@v6 - - uses: dorny/paths-filter@v3 - id: filter - with: - filters: | - full: - - '.github/workflows/tests.yml' - - 'src/**' - - 'tests/**' - - 'examples/**' - outputs: - full: ${{ steps.filter.outputs.full }} - - pre-commit: - needs: changes - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: actions/setup-python@v6 - with: - python-version: "3.x" - - uses: denoland/setup-deno@v2 - with: - deno-version: ${{ env.DENO_V }} - - name: Install tofu - run: | - curl --proto '=https' --tlsv1.2 -fsSL https://get.opentofu.org/install-opentofu.sh -o install-opentofu.sh - chmod +x install-opentofu.sh - ./install-opentofu.sh --install-method deb - rm -f install-opentofu.sh - - - shell: bash - run: | - python -m pip install --upgrade pip - pip install pre-commit - pre-commit install - deno --version - pre-commit run --all-files - - test-full: - needs: [changes] - if: ${{ needs.changes.outputs.full == 'true' && github.event.pull_request.draft == false }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: denoland/setup-deno@v2 - with: - deno-version: ${{ env.DENO_V }} - - name: Download Install Script - run: curl -fsSL "https://raw.github.com/metatypedev/ghjk/$GHJK_VERSION/install.sh" -o install.sh - - name: Execute Install Script - run: yes | bash install.sh - - run: echo "$HOME/.local/bin" >> "$GITHUB_PATH" - - run: echo "BASH_ENV=$HOME/.local/share/ghjk/env.sh" >> "$GITHUB_ENV" - - uses: actions/setup-python@v6 - with: - python-version: "3.x" - - name: Install tofu - run: | - curl --proto '=https' --tlsv1.2 -fsSL https://get.opentofu.org/install-opentofu.sh -o install-opentofu.sh - chmod +x install-opentofu.sh - ./install-opentofu.sh --install-method deb - rm -f install-opentofu.sh - - uses: actions/setup-node@v6 - with: - node-version: 18 - - name: setup start-server-and-test - run: npm install -g start-server-and-test - - shell: bash - env: - AUTH_TYPE: "default" - LOG_LEVEL: "DEBUG" - ENV: "DEV" - S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }} - S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }} - OPENSTACK_USERNAME: ${{ secrets.OPENSTACK_USERNAME }} - OPENSTACK_PASSWORD: ${{ secrets.OPENSTACK_PASSWORD }} - OPENSTACK_PROJECT: ${{ secrets.OPENSTACK_PROJECT }} - AWS_ACCESS_KEY_ID: ${{ secrets.OPENSTACK_USERNAME }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.OPENSTACK_PASSWORD }} - run: | - # run all tests - deno --version - ghjk x dev-compose s3 - sleep 20 - - deno install - - # ghjk x setup-auth - npx start-server-and-test 'deno serve -A --unstable-kv src/main.ts' http://0.0.0.0:8000/ 'deno test -A' diff --git a/.gitignore b/.gitignore index c8be0ce..7f896dd 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,4 @@ token *.db-shm *.db-wal .vscode +symlinks diff --git a/.infisical.json b/.infisical.json new file mode 100644 index 0000000..a6af04a --- /dev/null +++ b/.infisical.json @@ -0,0 +1,5 @@ +{ + "workspaceId": "39bbe4e4-20c2-42fa-8a6c-1bcafcc74faf", + "defaultEnvironment": "", + "gitBranchToEnvironmentMapping": null +} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b600640..96b71d2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v6.0.0 hooks: - id: check-added-large-files exclude: tests/res @@ -39,9 +39,14 @@ repos: types: - ts - repo: https://github.com/tofuutils/pre-commit-opentofu - rev: v1.0.3 + rev: v2.2.2 hooks: - id: tofu_fmt + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.36.0 + hooks: + - id: check-dependabot + - id: check-github-workflows # - repo: https://github.com/shellcheck-py/shellcheck-py # rev: v0.10.0.1 # hooks: diff --git a/AGENTS.md b/AGENTS.md index b3db2d5..4410833 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,8 +1,15 @@ - We're using the effects library https://effect.website/llms.txt - Their HTTP implementation is described in ./HTTP_PLATFORM.md + - **ALWAYS** use `@effect/platform/HttpClient` instead of native `fetch` for + all HTTP requests. - Prefer generators over effect piping. - Use methods on `Effect.Option` like `Option.isNone` instead of looking at _tag. + - **NEVER** use standard `try/catch` or `try/finally` blocks around `yield*` + in Effect generators. Use `Effect.addFinalizer`, `Effect.try`, + `Effect.catchAll`, or `Effect.orElse`. + - **ALWAYS** use the `Config` module from Effect for environment variable + access instead of `Deno.env.get`. - **NEVER** assume default values using `??` or ternary operators for critical configuration or external input (e.g., `bucket.region ?? "us-east-1"`, `request.headers.host ?? "localhost"`). Always fail explicitly with a diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 96c5f88..219781a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,8 +5,8 @@ - `src/Domain`: Core logic and data models. Contains Effect Schemas for global configuration and logic for bucket matching. -- `src/Config`: Application configuration loading. Defines the AppConfig service - layer. +- `src/Config`: Application configuration loading. Defines the HeraldConfig + service layer. - `src/Services`: Shared service abstractions and implementations. diff --git a/README.md b/README.md index 12531c4..cf44782 100644 --- a/README.md +++ b/README.md @@ -18,307 +18,65 @@
InvalidArgument