Add shared actions and workflows
This commit is contained in:
parent
cfda47d987
commit
2db080690f
56
actions/lint-test-build/action.yml
Normal file
56
actions/lint-test-build/action.yml
Normal file
@ -0,0 +1,56 @@
|
||||
name: 'Lint, Test, and Build'
|
||||
description: 'Run lint, test, and build steps for Node.js projects'
|
||||
|
||||
inputs:
|
||||
working-directory:
|
||||
description: 'Working directory for commands'
|
||||
required: true
|
||||
flavor:
|
||||
description: 'Build flavor (dev or prod) - lint and test only run for dev'
|
||||
required: false
|
||||
default: 'dev'
|
||||
run-lint:
|
||||
description: 'Whether to run lint step'
|
||||
required: false
|
||||
default: 'true'
|
||||
lint-command:
|
||||
description: 'Command to run for linting'
|
||||
required: false
|
||||
default: 'npm run lint'
|
||||
run-test:
|
||||
description: 'Whether to run test step'
|
||||
required: false
|
||||
default: 'true'
|
||||
test-command:
|
||||
description: 'Command to run for testing'
|
||||
required: false
|
||||
default: 'npm run test'
|
||||
run-build:
|
||||
description: 'Whether to run build step'
|
||||
required: false
|
||||
default: 'true'
|
||||
build-command:
|
||||
description: 'Command to run for building'
|
||||
required: false
|
||||
default: 'npm run build'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Lint
|
||||
if: inputs.run-lint == 'true' && inputs.flavor == 'dev'
|
||||
shell: bash
|
||||
run: ${{ inputs.lint-command }}
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
- name: Test
|
||||
if: inputs.run-test == 'true' && inputs.flavor == 'dev'
|
||||
shell: bash
|
||||
run: ${{ inputs.test-command }}
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
- name: Build
|
||||
if: inputs.run-build == 'true'
|
||||
shell: bash
|
||||
run: ${{ inputs.build-command }}
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
75
actions/node-ci/action.yml
Normal file
75
actions/node-ci/action.yml
Normal file
@ -0,0 +1,75 @@
|
||||
name: 'Node.js CI Setup'
|
||||
description: 'Checkout repository, setup Node.js, cache node_modules, and install dependencies'
|
||||
|
||||
inputs:
|
||||
node-version:
|
||||
description: 'Node.js version to use'
|
||||
required: false
|
||||
default: '24.13.0'
|
||||
checkout-path:
|
||||
description: 'Path to checkout the repository'
|
||||
required: false
|
||||
default: '.'
|
||||
working-directory:
|
||||
description: 'Working directory for npm install (relative to checkout-path)'
|
||||
required: true
|
||||
flavor:
|
||||
description: 'Build flavor (dev or prod)'
|
||||
required: false
|
||||
default: 'dev'
|
||||
cache-prefix:
|
||||
description: 'Prefix for cache key'
|
||||
required: false
|
||||
default: 'node'
|
||||
checkout-submodules:
|
||||
description: 'Whether to checkout submodules'
|
||||
required: false
|
||||
default: 'false'
|
||||
checkout-ref:
|
||||
description: 'Git ref to checkout (branch, tag, or SHA)'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: ${{ inputs.checkout-path }}
|
||||
ref: ${{ inputs.checkout-ref || github.ref }}
|
||||
|
||||
- name: Checkout submodules
|
||||
if: inputs.checkout-submodules == 'true'
|
||||
shell: bash
|
||||
run: git submodule update --init --recursive
|
||||
working-directory: ${{ inputs.checkout-path }}/${{ inputs.working-directory }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '${{ inputs.checkout-path }}/${{ inputs.working-directory }}/package-lock.json'
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ inputs.checkout-path }}/${{ inputs.working-directory }}/node_modules
|
||||
key: ${{ runner.os }}-${{ inputs.cache-prefix }}-${{ inputs.flavor }}-node-${{ inputs.node-version }}-${{ hashFiles(format('{0}/{1}/package-lock.json', inputs.checkout-path, inputs.working-directory)) }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ inputs.cache-prefix }}-${{ inputs.flavor }}-node-${{ inputs.node-version }}-
|
||||
${{ runner.os }}-${{ inputs.cache-prefix }}-${{ inputs.flavor }}-
|
||||
|
||||
- name: Install dependencies (dev)
|
||||
if: inputs.flavor == 'dev'
|
||||
shell: bash
|
||||
run: npm ci
|
||||
working-directory: ${{ inputs.checkout-path }}/${{ inputs.working-directory }}
|
||||
|
||||
- name: Install dependencies (prod)
|
||||
if: inputs.flavor == 'prod'
|
||||
shell: bash
|
||||
run: npm ci --omit=dev --omit=optional
|
||||
working-directory: ${{ inputs.checkout-path }}/${{ inputs.working-directory }}
|
||||
62
actions/restore-assets/action.yml
Normal file
62
actions/restore-assets/action.yml
Normal file
@ -0,0 +1,62 @@
|
||||
name: 'Restore Cached Assets'
|
||||
description: 'Restore cached mining pool and promo video assets from cache and/or artifacts'
|
||||
|
||||
inputs:
|
||||
frontend-path:
|
||||
description: 'Path to frontend directory'
|
||||
required: true
|
||||
use-artifacts:
|
||||
description: 'Whether to download from artifacts (requires cache job to have run first)'
|
||||
required: false
|
||||
default: 'true'
|
||||
|
||||
outputs:
|
||||
mining-pool-cache-hit:
|
||||
description: 'Whether mining pool assets were found in cache'
|
||||
value: ${{ steps.cache-mining-pool-restore.outputs.cache-hit }}
|
||||
promo-video-cache-hit:
|
||||
description: 'Whether promo video assets were found in cache'
|
||||
value: ${{ steps.cache-promo-video-restore.outputs.cache-hit }}
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Restore cached mining pool assets
|
||||
continue-on-error: true
|
||||
id: cache-mining-pool-restore
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: mining-pool-assets.zip
|
||||
key: mining-pool-assets-cache
|
||||
|
||||
- name: Restore cached promo video assets
|
||||
continue-on-error: true
|
||||
id: cache-promo-video-restore
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: promo-video-assets.zip
|
||||
key: promo-video-assets-cache
|
||||
|
||||
- name: Download mining pool artifact
|
||||
if: inputs.use-artifacts == 'true'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: mining-pool-assets
|
||||
continue-on-error: true
|
||||
|
||||
- name: Download promo video artifact
|
||||
if: inputs.use-artifacts == 'true'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: promo-video-assets
|
||||
continue-on-error: true
|
||||
|
||||
- name: Unzip mining pool assets
|
||||
shell: bash
|
||||
continue-on-error: true
|
||||
run: unzip -o mining-pool-assets.zip -d ${{ inputs.frontend-path }}/src/resources/mining-pools
|
||||
|
||||
- name: Unzip promo video assets
|
||||
shell: bash
|
||||
continue-on-error: true
|
||||
run: unzip -o promo-video-assets.zip -d ${{ inputs.frontend-path }}/src/resources/promo-video
|
||||
56
actions/setup-rust/action.yml
Normal file
56
actions/setup-rust/action.yml
Normal file
@ -0,0 +1,56 @@
|
||||
name: 'Setup Rust Toolchain'
|
||||
description: 'Read rust-toolchain file, cache Rust dependencies, and install the toolchain'
|
||||
|
||||
inputs:
|
||||
working-directory:
|
||||
description: 'Working directory (repository root)'
|
||||
required: true
|
||||
rust-toolchain-path:
|
||||
description: 'Path to rust-toolchain file relative to working-directory'
|
||||
required: false
|
||||
default: 'rust/gbt/rust-toolchain'
|
||||
cargo-lock-path:
|
||||
description: 'Path pattern for Cargo.lock files relative to working-directory'
|
||||
required: false
|
||||
default: 'rust/gbt/**/Cargo.lock'
|
||||
rust-target-path:
|
||||
description: 'Path to Rust target directory relative to working-directory'
|
||||
required: false
|
||||
default: 'rust/gbt/target/'
|
||||
flavor:
|
||||
description: 'Build flavor for cache key differentiation'
|
||||
required: false
|
||||
default: 'dev'
|
||||
|
||||
outputs:
|
||||
toolchain:
|
||||
description: 'The Rust toolchain version that was installed'
|
||||
value: ${{ steps.gettoolchain.outputs.toolchain }}
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Read rust-toolchain file
|
||||
id: gettoolchain
|
||||
shell: bash
|
||||
run: echo "toolchain=$(cat ./${{ inputs.rust-toolchain-path }})" >> $GITHUB_OUTPUT
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
- name: Cache Rust dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
${{ inputs.working-directory }}/${{ inputs.rust-target-path }}
|
||||
key: ${{ runner.os }}-cargo-${{ inputs.flavor }}-${{ hashFiles(format('{0}/{1}', inputs.working-directory, inputs.cargo-lock-path)) }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cargo-${{ inputs.flavor }}-
|
||||
${{ runner.os }}-cargo-
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561
|
||||
with:
|
||||
toolchain: ${{ steps.gettoolchain.outputs.toolchain }}
|
||||
84
actions/sync-assets/action.yml
Normal file
84
actions/sync-assets/action.yml
Normal file
@ -0,0 +1,84 @@
|
||||
name: 'Sync and Cache Assets'
|
||||
description: 'Sync assets from CDN, zip, upload as artifacts, and save to cache'
|
||||
|
||||
inputs:
|
||||
frontend-path:
|
||||
description: 'Path to frontend directory'
|
||||
required: true
|
||||
github-token:
|
||||
description: 'GitHub token for API access'
|
||||
required: true
|
||||
sync-command:
|
||||
description: 'npm command to run for syncing assets'
|
||||
required: false
|
||||
default: 'npm run sync-assets-dev'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Restore cached mining pool assets
|
||||
continue-on-error: true
|
||||
id: cache-mining-pool-restore
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: mining-pool-assets.zip
|
||||
key: mining-pool-assets-cache
|
||||
|
||||
- name: Restore cached promo video assets
|
||||
continue-on-error: true
|
||||
id: cache-promo-video-restore
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: promo-video-assets.zip
|
||||
key: promo-video-assets-cache
|
||||
|
||||
- name: Unzip mining pool assets before sync
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
run: unzip -o mining-pool-assets.zip -d ${{ inputs.frontend-path }}/src/resources/mining-pools
|
||||
|
||||
- name: Unzip promo video assets before sync
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
run: unzip -o promo-video-assets.zip -d ${{ inputs.frontend-path }}/src/resources/promo-video
|
||||
|
||||
- name: Sync assets
|
||||
shell: bash
|
||||
run: ${{ inputs.sync-command }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ inputs.github-token }}
|
||||
MEMPOOL_CDN: 1
|
||||
VERBOSE: 1
|
||||
working-directory: ${{ inputs.frontend-path }}
|
||||
|
||||
- name: Zip mining pool assets
|
||||
shell: bash
|
||||
run: zip -jrq mining-pool-assets.zip ${{ inputs.frontend-path }}/src/resources/mining-pools/*
|
||||
|
||||
- name: Zip promo video assets
|
||||
shell: bash
|
||||
run: zip -jrq promo-video-assets.zip ${{ inputs.frontend-path }}/src/resources/promo-video/*
|
||||
|
||||
- name: Upload mining pool assets artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mining-pool-assets
|
||||
path: mining-pool-assets.zip
|
||||
|
||||
- name: Upload promo video assets artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: promo-video-assets
|
||||
path: promo-video-assets.zip
|
||||
|
||||
- name: Save mining pool assets cache
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: mining-pool-assets.zip
|
||||
key: mining-pool-assets-cache
|
||||
|
||||
- name: Save promo video assets cache
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: promo-video-assets.zip
|
||||
key: promo-video-assets-cache
|
||||
156
workflows/project-board-automation.yml
Normal file
156
workflows/project-board-automation.yml
Normal file
@ -0,0 +1,156 @@
|
||||
# Reusable workflow: Automate project board management
|
||||
# - Add newly created issues to project board
|
||||
# - Set status to "Review Needed" when a reviewer is requested on a non-draft PR
|
||||
name: Project Board Automation
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
project-number:
|
||||
description: 'The project board number'
|
||||
required: false
|
||||
type: number
|
||||
default: 8
|
||||
runs-on:
|
||||
description: 'Runner to use for the job'
|
||||
required: false
|
||||
type: string
|
||||
default: 'ubuntu-latest'
|
||||
handle-issues:
|
||||
description: 'Whether to handle adding issues to project board'
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
secrets:
|
||||
PROJECT_TOKEN:
|
||||
description: 'PAT with project write access'
|
||||
required: true
|
||||
PROJECT_ID:
|
||||
description: 'The project unique identifier'
|
||||
required: true
|
||||
STATUS_FIELD_ID:
|
||||
description: 'The Status field unique identifier'
|
||||
required: true
|
||||
REVIEW_NEEDED_OPTION_ID:
|
||||
description: 'The Review Needed option unique identifier'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
manage-project-board:
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
steps:
|
||||
- name: Update Project Board
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.PROJECT_TOKEN }}
|
||||
script: |
|
||||
const projectNumber = ${{ inputs.project-number }};
|
||||
const handleIssues = ${{ inputs.handle-issues }};
|
||||
|
||||
// Skip draft PRs
|
||||
if (context.eventName === 'pull_request' && context.payload.pull_request.draft) {
|
||||
console.log('PR is a draft, skipping Review Needed status...');
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle new issues - add to project
|
||||
if (context.eventName === 'issues' && handleIssues) {
|
||||
const addMutation = `
|
||||
mutation($projectId: ID!, $contentId: ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $projectId, contentId: $contentId}) {
|
||||
item { id }
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
try {
|
||||
await github.graphql(addMutation, {
|
||||
projectId: "${{ secrets.PROJECT_ID }}",
|
||||
contentId: context.payload.issue.node_id
|
||||
});
|
||||
|
||||
console.log(`Successfully added issue to project #${projectNumber}`);
|
||||
} catch (error) {
|
||||
// Handle case where the issue is already in the project
|
||||
const errors = error && error.errors ? error.errors : [];
|
||||
const alreadyInProject = errors.some(e =>
|
||||
typeof e.message === 'string' &&
|
||||
e.message.toLowerCase().includes('already') &&
|
||||
e.message.toLowerCase().includes('project')
|
||||
);
|
||||
|
||||
if (alreadyInProject) {
|
||||
console.log(`Issue is already in project #${projectNumber}, skipping add.`);
|
||||
} else {
|
||||
console.error(`Failed to add issue to project #${projectNumber}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle PR review_requested - update status to "Review Needed"
|
||||
if (context.eventName === 'pull_request') {
|
||||
// GraphQL query to find the PR's project items
|
||||
const query = `
|
||||
query($owner: String!, $repo: String!, $pr: Int!) {
|
||||
repository(owner: $owner, name: $repo) {
|
||||
pullRequest(number: $pr) {
|
||||
projectItems(first: 10) {
|
||||
nodes {
|
||||
id
|
||||
project {
|
||||
number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// Execute the query with current repo/PR context
|
||||
const result = await github.graphql(query, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pr: context.payload.pull_request.number
|
||||
});
|
||||
|
||||
// Find the project item that belongs to the target project
|
||||
const projectItems = result.repository.pullRequest.projectItems.nodes;
|
||||
const projectItem = projectItems.find(item => item.project.number === projectNumber);
|
||||
|
||||
// Exit early if PR isn't in the target project
|
||||
if (!projectItem) {
|
||||
console.log(`PR is not in project #${projectNumber}, skipping...`);
|
||||
return;
|
||||
}
|
||||
|
||||
// GraphQL mutation to update the Status field
|
||||
const mutation = `
|
||||
mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) {
|
||||
updateProjectV2ItemFieldValue(
|
||||
input: {
|
||||
projectId: $projectId
|
||||
itemId: $itemId
|
||||
fieldId: $fieldId
|
||||
value: { singleSelectOptionId: $optionId }
|
||||
}
|
||||
) {
|
||||
projectV2Item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// Execute the mutation
|
||||
await github.graphql(mutation, {
|
||||
projectId: "${{ secrets.PROJECT_ID }}",
|
||||
itemId: projectItem.id,
|
||||
fieldId: "${{ secrets.STATUS_FIELD_ID }}",
|
||||
optionId: "${{ secrets.REVIEW_NEEDED_OPTION_ID }}"
|
||||
});
|
||||
|
||||
console.log('Successfully updated project status to Review Needed');
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user