diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..da5267a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: +- package-ecosystem: npm + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 10 + versioning-strategy: increase-if-necessary +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly diff --git a/.github/workflows/build-preview.yml b/.github/workflows/build-preview.yml new file mode 100644 index 0000000..e75b697 --- /dev/null +++ b/.github/workflows/build-preview.yml @@ -0,0 +1,32 @@ +name: Build Preview + +on: + pull_request: + branches: + - main + +permissions: + contents: read + +jobs: + build-preview: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: '16' + - run: npm ci + - run: ./tasks/build-website.sh -v dev -l dev + - uses: actions/upload-artifact@v3 + with: + name: site + path: build/site + - name: Store pull request number for later use + run: | + mkdir -p build/pr + echo ${{github.event.number}} > build/pr/number + - uses: actions/upload-artifact@v3 + with: + name: pr + path: build/pr diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml new file mode 100644 index 0000000..6b2c07e --- /dev/null +++ b/.github/workflows/deploy-preview.yml @@ -0,0 +1,118 @@ +name: Deploy Website (Preview) + +on: + workflow_run: + workflows: ["Build Preview"] + types: + - completed + +jobs: + deploy-preview: + runs-on: ubuntu-latest + if: ${{github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'}} + steps: + - uses: actions/setup-node@v3 + with: + node-version: '16' + - run: npm install --global netlify-cli@6 + - run: npm install unzipper@0.10 + + - name: Get pull request number + uses: actions/github-script@v6 + id: pull-request-number + with: + result-encoding: string + script: | + const unzipper = require('unzipper'); + + const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{github.event.workflow_run.id}} + }); + + const artifact = artifacts.data.artifacts.filter( + artifact => artifact.name === 'pr' + )[0]; + + if (!artifact) { + throw new Error('No pr artifact found'); + } + + const download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: artifact.id, + archive_format: 'zip' + }); + + const directory = await unzipper.Open.buffer(Buffer.from(download.data)); + const file = directory.files.find(d => d.path === 'number'); + const content = await file.buffer(); + return content.toString(); + + - uses: dawidd6/action-download-artifact@v2 + with: + github_token: ${{secrets.GITHUB_TOKEN}} + workflow: build-preview.yml + pr: ${{steps.pull-request-number.outputs.result}} + name: site + path: build/site + + - name: Deploy to Netlify + env: + NETLIFY_AUTH_TOKEN: ${{secrets.NETLIFY_AUTH_TOKEN}} + NETLIFY_SITE_ID: ${{secrets.NETLIFY_SITE_ID}} + run: netlify deploy --dir=build/site --alias=deploy-preview-${{steps.pull-request-number.outputs.result}} + + - name: Add comment to pull request + uses: actions/github-script@v6 + with: + script: | + const pullRequestNumber = parseInt(${{steps.pull-request-number.outputs.result}}, 10); + + const start = ':package:'; + const author = 'github-actions[bot]'; + + const comments = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pullRequestNumber + }); + + const commentExists = comments.data.some( + comment => comment.user.login === author && comment.body.startsWith(start) + ); + + if (!commentExists) { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pullRequestNumber, + body: `${start} Preview the website for this branch here: https://deploy-preview-${pullRequestNumber}--ol-site.netlify.app/.` + }); + } else { + console.log(`Preview URL comment already added to PR #${pullRequestNumber}`); + } + + - name: Clean up artifact + uses: actions/github-script@v6 + with: + result-encoding: string + script: | + const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{github.event.workflow_run.id}} + }); + const artifact = artifacts.data.artifacts.filter( + artifact => artifact.name === 'site' + )[0]; + if (!artifact) { + throw new Error('No site artifact found'); + } + await github.rest.actions.deleteArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: artifact.id + }); diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..9eabec7 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,71 @@ +name: Deploy Website + +on: + push: + branches: + - main + tags: + - 'v*.*.*' + +concurrency: + group: "deploy" + +jobs: + deploy-branch: + if: startsWith(github.ref, 'refs/heads/') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: '18' + - name: Install dependencies + run: npm ci + - name: Build Website + run: ./tasks/build-website.sh -l $(node tasks/get-latest-release.js) + - name: Check out openlayers.github.io + uses: actions/checkout@v4 + with: + repository: openlayers/openlayers.github.io + ssh-key: ${{ secrets.OPENLAYERS_GITHUB_IO_KEY }} + path: openlayers.github.io + - run: | + cp -r build/site/* openlayers.github.io/dist/ + cd openlayers.github.io + if [ -n "$(git status --porcelain)" ]; then + git config user.name "$(git --no-pager log --format=format:'%an' -n 1)" + git config user.email "$(git --no-pager log --format=format:'%ae' -n 1)" + git add . + git commit -m "Website updates" + git push origin main + fi + deploy-tag: + if: startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: '18' + - name: Install dependencies + run: npm ci + - name: Assert Latest Release + run: node tasks/newest-tag.js --tag ${GITHUB_REF_NAME} + - name: Build Website + run: ./tasks/build-website.sh -l ${GITHUB_REF_NAME} -v ${GITHUB_REF_NAME} + - name: Check out openlayers.github.io + uses: actions/checkout@v4 + with: + repository: openlayers/openlayers.github.io + ssh-key: ${{ secrets.OPENLAYERS_GITHUB_IO_KEY }} + path: openlayers.github.io + - run: | + cp -r build/site/* openlayers.github.io/dist/ + cd openlayers.github.io + if [ -n "$(git status --porcelain)" ]; then + git config user.name "$(git --no-pager log --format=format:'%an' -n 1)" + git config user.email "$(git --no-pager log --format=format:'%ae' -n 1)" + git add . + git commit -m "Website updates" + git push origin main + fi diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..49bc27d --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,54 @@ +name: Publish Package + +on: + push: + branches: + - main + tags: + - 'v*.*.*' + +permissions: + contents: read + id-token: write + +jobs: + publish-branch: + if: startsWith(github.ref, 'refs/heads/') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + - name: Install dependencies + run: npm ci + - name: Publish + run: | + VERSION=$(node tasks/next-dev-version.js) + npm --no-git-tag-version version ${VERSION} + npm run build-package + cd build/ol + npm publish --provenance --tag dev + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} + publish-tag: + if: startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + - name: Install dependencies + run: npm ci + - name: Assert Latest Release + run: node tasks/newest-tag.js --tag ${GITHUB_REF_NAME} + - name: Publish + run: | + npm run build-package + cd build/ol + npm publish --provenance + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..56700d9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,21 @@ +name: Create Release + +on: + push: + tags: + - 'v*.*.*' + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: '18' + - name: Install dependencies + run: npm ci + - name: Build Release Assets + run: ./tasks/build-website.sh -l ${GITHUB_REF_NAME} -v ${GITHUB_REF_NAME} + - name: Create Release + run: node tasks/create-release.js --token ${{secrets.GITHUB_TOKEN}} --tag ${GITHUB_REF_NAME} --package build/${GITHUB_REF_NAME}-package.zip --site build/${GITHUB_REF_NAME}-site.zip diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..63699a4 --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,34 @@ +name: "Security Scan" + +on: + push: + branches: + - main + pull_request: + branches: + - main + schedule: + - cron: '0 0 * * 0' # At 00:00 on Sunday + +permissions: + contents: read + +jobs: + codeql: + permissions: + actions: read # for github/codeql-action/init to get workflow details + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/analyze to upload SARIF results + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: javascript + source-root: src + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..989862d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,35 @@ +name: Test + +on: + push: + branches: + - main + pull_request: + branches: + - main + +permissions: + contents: read + +jobs: + test: + name: Test + runs-on: ubuntu-latest + + steps: + - name: Clone Repository + uses: actions/checkout@v4 + + - name: Install Node + uses: actions/setup-node@v3 + with: + node-version: '20' + + - name: Install Dependencies + run: npm ci + + - name: Run Tests + run: npm test + + - name: Build + run: npm run build diff --git a/.gitignore b/.gitignore index c2658d7..1156ada 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules/ +/dist/ diff --git a/package.json b/package.json index f0a0e00..58e97bf 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "lint": "eslint cases", "typecheck": "tsc --pretty", "test": "npm run lint && npm run typecheck", - "start": "vite dev" + "start": "vite dev", + "build": "vite build" }, "repository": { "type": "git",