Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false
12 changes: 12 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
* text=auto eol=lf
*.bat text eol=crlf
*.cmd text eol=crlf
*.png binary
*.jpg binary
*.jpeg binary
*.webp binary
*.ico binary
*.woff2 binary
*.zip binary
*.tgz binary
package-lock.json linguist-generated=true
2 changes: 2 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/sh
exec npm run check
2 changes: 2 additions & 0 deletions .githooks/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/sh
exec npm run check
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @deku2026
22 changes: 22 additions & 0 deletions .github/ISSUE_TEMPLATE/bug.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Bug report
description: Report a reproducible Web or delivery problem.
body:
- type: markdown
attributes:
value: "Report security issues privately using the Security policy. Never attach tokens or personal account data."
- type: textarea
id: reproduce
attributes:
label: Steps, expected and actual behavior
validations:
required: true
- type: textarea
id: environment
attributes:
label: Version, browser and operating system
validations:
required: true
- type: textarea
id: evidence
attributes:
label: Sanitized logs or screenshots
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Private security report
url: https://github.com/ArcForges/Web/security/advisories/new
about: Report a vulnerability without disclosing it publicly.
13 changes: 13 additions & 0 deletions .github/ISSUE_TEMPLATE/feature.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: Feature proposal
description: Propose a concrete improvement within the product scope.
body:
- type: textarea
id: problem
attributes:
label: User problem and desired behavior
validations:
required: true
- type: textarea
id: acceptance
attributes:
label: Acceptance criteria and scope
13 changes: 13 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Change

Describe the observable change and why it is needed.

## Validation

- [ ] `npm run check`
- [ ] `npm run build` and `npm run test:e2e`
- [ ] Candidate verified; relevant notices/docs updated

## Deployment

State any configuration change, recovery steps and evidence that still requires real Cloudflare/C# verification. Do not include credentials.
51 changes: 51 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
day: monday
open-pull-requests-limit: 5
versioning-strategy: increase
groups:
react-and-router:
patterns:
[
"react",
"react-dom",
"react-router",
"@react-router/*",
"@types/react",
"@types/react-dom",
]
contracts:
patterns: ["@arcforges/*", "@bufbuild/*", "@connectrpc/*"]
cloudflare:
patterns: ["wrangler", "@cloudflare/*"]
tooling:
patterns: ["*"]
exclude-patterns:
[
"react",
"react-dom",
"react-router",
"@react-router/*",
"@types/react",
"@types/react-dom",
"@arcforges/*",
"@bufbuild/*",
"@connectrpc/*",
"wrangler",
"@cloudflare/*",
]
ignore:
- dependency-name: "@types/node"
versions: [">=25"]
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
day: monday
groups:
github-actions:
patterns: ["*"]
211 changes: 211 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
name: CI
on:
pull_request:
push:
branches: [main]
workflow_dispatch:
schedule:
- cron: "31 5 * * 1"

permissions:
contents: read

concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
CI: "true"
WRANGLER_SEND_METRICS: "false"

jobs:
source:
name: Source (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: .node-version
# Avoid blocking Windows validation on optional cache upload retries.
cache: ${{ matrix.os == 'ubuntu-latest' && 'npm' || '' }}
package-manager-cache: false
- run: npm ci --ignore-scripts
- run: npm run check

quality:
name: Dependency audit and repository checks
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: .node-version
cache: npm
- run: npm ci --ignore-scripts
- run: npm audit --audit-level=high
- name: Check workflow syntax
run: |
curl --fail --silent --show-error --location https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_amd64.tar.gz --output "$RUNNER_TEMP/actionlint.tar.gz"
echo '8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8 '"$RUNNER_TEMP/actionlint.tar.gz" | sha256sum --check
tar -xzf "$RUNNER_TEMP/actionlint.tar.gz" -C "$RUNNER_TEMP" actionlint
"$RUNNER_TEMP/actionlint" -color
- name: Scan Git history for secrets
run: docker run --rm --network none -v "$PWD:/repo:ro" zricethezav/gitleaks@sha256:c00b6bd0aeb3071cbcb79009cb16a60dd9e0a7c60e2be9ab65d25e6bc8abbb7f git /repo --redact=100 --no-banner

dependency-review:
name: Dependency review
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
with:
fail-on-severity: high

codeql:
name: CodeQL (${{ matrix.language }})
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [javascript-typescript, actions]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4
with:
languages: ${{ matrix.language }}
build-mode: none
- uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4
with:
category: /language:${{ matrix.language }}

candidate:
name: Build and test candidate
needs: [source, quality]
runs-on: ubuntu-latest
timeout-minutes: 20
outputs:
artifact-id: ${{ steps.upload.outputs.artifact-id }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: .node-version
cache: npm
- run: npm ci --ignore-scripts
- run: npm run build
- run: npx --no-install playwright install --with-deps chromium firefox webkit
- run: npm run test:e2e
- run: npm run verify:candidate
- id: upload
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: web-candidate-${{ github.run_id }}-${{ github.run_attempt }}
path: artifacts/candidate
if-no-files-found: error
retention-days: 30
- name: Preserve browser evidence
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: browser-evidence-${{ github.run_id }}-${{ github.run_attempt }}
path: |
playwright-report
test-results
retention-days: 14

verify:
name: Verify
if: always()
needs: [source, quality, dependency-review, codeql, candidate]
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: Require every applicable check
env:
SOURCE: ${{ needs.source.result }}
QUALITY: ${{ needs.quality.result }}
REVIEW: ${{ needs.dependency-review.result }}
CODEQL: ${{ needs.codeql.result }}
CANDIDATE: ${{ needs.candidate.result }}
EVENT: ${{ github.event_name }}
run: |
test "$SOURCE" = success
test "$QUALITY" = success
test "$CODEQL" = success
test "$CANDIDATE" = success
if [ "$EVENT" = pull_request ]; then test "$REVIEW" = success; else test "$REVIEW" = skipped; fi

deploy:
name: Deploy and verify Cloudflare
if: >-
!cancelled() && github.event_name == 'push' && github.ref == 'refs/heads/main'
&& needs.verify.result == 'success' && needs.candidate.result == 'success'
needs: [verify, candidate]
runs-on: ubuntu-latest
timeout-minutes: 15
environment: cloudflare
concurrency:
group: cloudflare-main
cancel-in-progress: false
permissions:
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: .node-version
cache: npm
- run: npm ci --ignore-scripts
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
artifact-ids: ${{ needs.candidate.outputs.artifact-id }}
path: artifacts/candidate
merge-multiple: true
- run: npm run verify:candidate
- run: npm run deploy
env:
CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
GH_TOKEN: ${{ github.token }}
- run: npm run test:live
- name: Create verified prerelease
env:
GH_TOKEN: ${{ github.token }}
run: |
VERSION=$(node --input-type=module -e 'import fs from "node:fs"; console.log(JSON.parse(fs.readFileSync("artifacts/candidate/manifest.json")).version)')
tar -czf "artifacts/web-${VERSION}.tar.gz" -C artifacts/candidate .
gh release create "web-${VERSION}" "artifacts/web-${VERSION}.tar.gz" artifacts/deployment.json --target "$GITHUB_SHA" --title "Web ${VERSION}" --prerelease --generate-notes
- name: Preserve deployment evidence
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: cloudflare-evidence-${{ github.run_id }}-${{ github.run_attempt }}
path: artifacts/deployment.json
if-no-files-found: warn
retention-days: 90
29 changes: 29 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
node_modules/
.worktree/
.wrangler/
**/.react-router/
**/build/
dist/
artifacts/
playwright-report/
test-results/
coverage/
*.tsbuildinfo
.vs/
.idea/
*.user
*.suo
bin/
obj/
*.log
.env
.env.*
!.env.example
.dev.vars
.dev.vars.*
*.pem
*.key
*.pfx
*.p12
.DS_Store
Thumbs.db
1 change: 1 addition & 0 deletions .node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24.21.0
4 changes: 4 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
save-exact=true
engine-strict=true
fund=false
ignore-scripts=true
Loading
Loading