Merge 686386f4cc into haskell-updates
This commit is contained in:
@@ -193,6 +193,9 @@ cffc27daf06c77c0d76bc35d24b929cb9d68c3c9
|
||||
# nixos/kanidm: inherit lib, nixfmt
|
||||
8f18393d380079904d072007fb19dc64baef0a3a
|
||||
|
||||
# fetchhg: format after refactoring with lib.extendMkDerivation and make overridable (#423539)
|
||||
34a5b1eb23129f8fb62c677e3760903f6d43228f
|
||||
|
||||
# fetchurl: nixfmt-rfc-style
|
||||
ce21e97a1f20dee15da85c084f9d1148d84f853b
|
||||
|
||||
|
||||
+10
-399
@@ -40,6 +40,11 @@ jobs:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
if: github.event_name != 'schedule' || github.repository_owner == 'NixOS'
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
sparse-checkout: |
|
||||
ci/labels
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install @actions/artifact bottleneck
|
||||
|
||||
@@ -64,406 +69,12 @@ jobs:
|
||||
github-token: ${{ steps.app-token.outputs.token || github.token }}
|
||||
retries: 3
|
||||
script: |
|
||||
const Bottleneck = require('bottleneck')
|
||||
const path = require('node:path')
|
||||
const { DefaultArtifactClient } = require('@actions/artifact')
|
||||
const { readFile, writeFile } = require('node:fs/promises')
|
||||
|
||||
const artifactClient = new DefaultArtifactClient()
|
||||
|
||||
const stats = {
|
||||
issues: 0,
|
||||
prs: 0,
|
||||
requests: 0,
|
||||
artifacts: 0
|
||||
}
|
||||
|
||||
// Rate-Limiting and Throttling, see for details:
|
||||
// https://github.com/octokit/octokit.js/issues/1069#throttling
|
||||
// https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api
|
||||
const allLimits = new Bottleneck({
|
||||
// Avoid concurrent requests
|
||||
maxConcurrent: 1,
|
||||
// Will be updated with first `updateReservoir()` call below.
|
||||
reservoir: 0
|
||||
require('./ci/labels/labels.cjs')({
|
||||
github,
|
||||
context,
|
||||
core,
|
||||
dry: context.eventName == 'pull_request'
|
||||
})
|
||||
// Pause between mutative requests
|
||||
const writeLimits = new Bottleneck({ minTime: 1000 }).chain(allLimits)
|
||||
github.hook.wrap('request', async (request, options) => {
|
||||
// Requests to the /rate_limit endpoint do not count against the rate limit.
|
||||
if (options.url == '/rate_limit') return request(options)
|
||||
// Search requests are in a different resource group, which allows 30 requests / minute.
|
||||
// We do less than a handful each run, so not implementing throttling for now.
|
||||
if (options.url.startsWith('/search/')) return request(options)
|
||||
stats.requests++
|
||||
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(options.method))
|
||||
return writeLimits.schedule(request.bind(null, options))
|
||||
else
|
||||
return allLimits.schedule(request.bind(null, options))
|
||||
})
|
||||
|
||||
async function updateReservoir() {
|
||||
let response
|
||||
try {
|
||||
response = await github.rest.rateLimit.get()
|
||||
} catch (err) {
|
||||
core.error(`Failed updating reservoir:\n${err}`)
|
||||
// Keep retrying on failed rate limit requests instead of exiting the script early.
|
||||
return
|
||||
}
|
||||
// Always keep 1000 spare requests for other jobs to do their regular duty.
|
||||
// They normally use below 100, so 1000 is *plenty* of room to work with.
|
||||
const reservoir = Math.max(0, response.data.resources.core.remaining - 1000)
|
||||
core.info(`Updating reservoir to: ${reservoir}`)
|
||||
allLimits.updateSettings({ reservoir })
|
||||
}
|
||||
await updateReservoir()
|
||||
// Update remaining requests every minute to account for other jobs running in parallel.
|
||||
const reservoirUpdater = setInterval(updateReservoir, 60 * 1000)
|
||||
|
||||
async function handlePullRequest(item) {
|
||||
const log = (k,v) => core.info(`PR #${item.number} - ${k}: ${v}`)
|
||||
|
||||
const pull_number = item.number
|
||||
|
||||
// This API request is important for the merge-conflict label, because it triggers the
|
||||
// creation of a new test merge commit. This is needed to actually determine the state of a PR.
|
||||
const pull_request = (await github.rest.pulls.get({
|
||||
...context.repo,
|
||||
pull_number
|
||||
})).data
|
||||
|
||||
const reviews = await github.paginate(github.rest.pulls.listReviews, {
|
||||
...context.repo,
|
||||
pull_number
|
||||
})
|
||||
|
||||
const approvals = new Set(
|
||||
reviews
|
||||
.filter(review => review.state == 'APPROVED')
|
||||
.map(review => review.user?.id)
|
||||
)
|
||||
|
||||
// After creation of a Pull Request, `merge_commit_sha` will be null initially:
|
||||
// The very first merge commit will only be calculated after a little while.
|
||||
// To avoid labeling the PR as conflicted before that, we wait a few minutes.
|
||||
// This is intentionally less than the time that Eval takes, so that the label job
|
||||
// running after Eval can indeed label the PR as conflicted if that is the case.
|
||||
const merge_commit_sha_valid = new Date() - new Date(pull_request.created_at) > 3 * 60 * 1000
|
||||
|
||||
const prLabels = {
|
||||
// We intentionally don't use the mergeable or mergeable_state attributes.
|
||||
// Those have an intermediate state while the test merge commit is created.
|
||||
// This doesn't work well for us, because we might have just triggered another
|
||||
// test merge commit creation by request the pull request via API at the start
|
||||
// of this function.
|
||||
// The attribute merge_commit_sha keeps the old value of null or the hash *until*
|
||||
// the new test merge commit has either successfully been created or failed so.
|
||||
// This essentially means we are updating the merge conflict label in two steps:
|
||||
// On the first pass of the day, we just fetch the pull request, which triggers
|
||||
// the creation. At this stage, the label is likely not updated, yet.
|
||||
// The second pass will then read the result from the first pass and set the label.
|
||||
'2.status: merge conflict': merge_commit_sha_valid && !pull_request.merge_commit_sha,
|
||||
'12.approvals: 1': approvals.size == 1,
|
||||
'12.approvals: 2': approvals.size == 2,
|
||||
'12.approvals: 3+': approvals.size >= 3,
|
||||
'12.first-time contribution':
|
||||
[ 'NONE', 'FIRST_TIMER', 'FIRST_TIME_CONTRIBUTOR' ].includes(pull_request.author_association),
|
||||
}
|
||||
|
||||
const { id: run_id, conclusion } = (await github.rest.actions.listWorkflowRuns({
|
||||
...context.repo,
|
||||
workflow_id: 'pr.yml',
|
||||
event: 'pull_request_target',
|
||||
exclude_pull_requests: true,
|
||||
head_sha: pull_request.head.sha
|
||||
})).data.workflow_runs[0] ??
|
||||
// TODO: Remove this after 2025-09-17, at which point all eval.yml artifacts will have expired.
|
||||
(await github.rest.actions.listWorkflowRuns({
|
||||
...context.repo,
|
||||
// In older PRs, we need eval.yml instead of pr.yml.
|
||||
workflow_id: 'eval.yml',
|
||||
event: 'pull_request_target',
|
||||
status: 'success',
|
||||
exclude_pull_requests: true,
|
||||
head_sha: pull_request.head.sha
|
||||
})).data.workflow_runs[0] ?? {}
|
||||
|
||||
// Newer PRs might not have run Eval to completion, yet.
|
||||
// Older PRs might not have an eval.yml workflow, yet.
|
||||
// In either case we continue without fetching an artifact on a best-effort basis.
|
||||
log('Last eval run', run_id ?? '<n/a>')
|
||||
|
||||
if (conclusion === 'success') {
|
||||
Object.assign(prLabels, {
|
||||
// We only set this label if the latest eval run was successful, because if it was not, it
|
||||
// *could* have requested reviewers. We will let the PR author fix CI first, before "escalating"
|
||||
// this PR to "needs: reviewer".
|
||||
// Since the first Eval run on a PR always sets rebuild labels, the same PR will be "recently
|
||||
// updated" for the next scheduled run. Thus, this label will still be set within a few minutes
|
||||
// after a PR is created, if required.
|
||||
// Note that a "requested reviewer" disappears once they have given a review, so we check
|
||||
// existing reviews, too.
|
||||
'9.needs: reviewer':
|
||||
!pull_request.draft &&
|
||||
pull_request.requested_reviewers.length == 0 &&
|
||||
reviews.length == 0,
|
||||
})
|
||||
}
|
||||
|
||||
const artifact = run_id && (await github.rest.actions.listWorkflowRunArtifacts({
|
||||
...context.repo,
|
||||
run_id,
|
||||
name: 'comparison'
|
||||
})).data.artifacts[0]
|
||||
|
||||
// Instead of checking the boolean artifact.expired, we will give us a minute to
|
||||
// actually download the artifact in the next step and avoid that race condition.
|
||||
// Older PRs, where the workflow run was already eval.yml, but the artifact was not
|
||||
// called "comparison", yet, will skip the download.
|
||||
const expired = !artifact || new Date(artifact?.expires_at ?? 0) < new Date(new Date().getTime() + 60 * 1000)
|
||||
log('Artifact expires at', artifact?.expires_at ?? '<n/a>')
|
||||
if (!expired) {
|
||||
stats.artifacts++
|
||||
|
||||
await artifactClient.downloadArtifact(artifact.id, {
|
||||
findBy: {
|
||||
repositoryName: context.repo.repo,
|
||||
repositoryOwner: context.repo.owner,
|
||||
token: core.getInput('github-token')
|
||||
},
|
||||
path: path.resolve(pull_number.toString()),
|
||||
expectedHash: artifact.digest
|
||||
})
|
||||
|
||||
const maintainers = new Set(Object.keys(
|
||||
JSON.parse(await readFile(`${pull_number}/maintainers.json`, 'utf-8'))
|
||||
).map(m => Number.parseInt(m, 10)))
|
||||
|
||||
const evalLabels = JSON.parse(await readFile(`${pull_number}/changed-paths.json`, 'utf-8')).labels
|
||||
|
||||
Object.assign(
|
||||
prLabels,
|
||||
// Ignore `evalLabels` if it's an array.
|
||||
// This can happen for older eval runs, before we switched to objects.
|
||||
// The old eval labels would have been set by the eval run,
|
||||
// so now they'll be present in `before`.
|
||||
// TODO: Simplify once old eval results have expired (~2025-10)
|
||||
(Array.isArray(evalLabels) ? undefined : evalLabels),
|
||||
{
|
||||
'12.approved-by: package-maintainer': Array.from(maintainers).some(m => approvals.has(m)),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return prLabels
|
||||
}
|
||||
|
||||
async function handle(item) {
|
||||
try {
|
||||
const log = (k,v,skip) => {
|
||||
core.info(`#${item.number} - ${k}: ${v}` + (skip ? ' (skipped)' : ''))
|
||||
return skip
|
||||
}
|
||||
|
||||
log('Last updated at', item.updated_at)
|
||||
log('URL', item.html_url)
|
||||
|
||||
const issue_number = item.number
|
||||
|
||||
const itemLabels = {}
|
||||
|
||||
if (item.pull_request) {
|
||||
stats.prs++
|
||||
Object.assign(itemLabels, await handlePullRequest(item))
|
||||
} else {
|
||||
stats.issues++
|
||||
}
|
||||
|
||||
const latest_event_at = new Date(
|
||||
(await github.paginate(
|
||||
github.rest.issues.listEventsForTimeline,
|
||||
{
|
||||
...context.repo,
|
||||
issue_number,
|
||||
per_page: 100
|
||||
}
|
||||
))
|
||||
.filter(({ event }) => [
|
||||
// These events are hand-picked from:
|
||||
// https://docs.github.com/en/rest/using-the-rest-api/issue-event-types?apiVersion=2022-11-28
|
||||
// Each of those causes a PR/issue to *not* be considered as stale anymore.
|
||||
// Most of these use created_at.
|
||||
'assigned',
|
||||
'commented', // uses updated_at, because that could be > created_at
|
||||
'committed', // uses committer.date
|
||||
'head_ref_force_pushed',
|
||||
'milestoned',
|
||||
'pinned',
|
||||
'ready_for_review',
|
||||
'renamed',
|
||||
'reopened',
|
||||
'review_dismissed',
|
||||
'review_requested',
|
||||
'reviewed', // uses submitted_at
|
||||
'unlocked',
|
||||
'unmarked_as_duplicate',
|
||||
].includes(event))
|
||||
.map(({ created_at, updated_at, committer, submitted_at }) => new Date(updated_at ?? created_at ?? submitted_at ?? committer.date))
|
||||
// Reverse sort by date value. The default sort() sorts by string representation, which is bad for dates.
|
||||
.sort((a,b) => b-a)
|
||||
.at(0) ?? item.created_at
|
||||
)
|
||||
log('latest_event_at', latest_event_at.toISOString())
|
||||
|
||||
const stale_at = new Date(new Date().setDate(new Date().getDate() - 180))
|
||||
|
||||
// Create a map (Label -> Boolean) of all currently set labels.
|
||||
// Each label is set to True and can be disabled later.
|
||||
const before = Object.fromEntries(
|
||||
(await github.paginate(github.rest.issues.listLabelsOnIssue, {
|
||||
...context.repo,
|
||||
issue_number
|
||||
}))
|
||||
.map(({ name }) => [name, true])
|
||||
)
|
||||
|
||||
Object.assign(itemLabels, {
|
||||
'2.status: stale': !before['1.severity: security'] && latest_event_at < stale_at,
|
||||
})
|
||||
|
||||
const after = Object.assign({}, before, itemLabels)
|
||||
|
||||
// No need for an API request, if all labels are the same.
|
||||
const hasChanges = Object.keys(after).some(name => (before[name] ?? false) != after[name])
|
||||
if (log('Has changes', hasChanges, !hasChanges))
|
||||
return;
|
||||
|
||||
// Skipping labeling on a pull_request event, because we have no privileges.
|
||||
const labels = Object.entries(after).filter(([,value]) => value).map(([name]) => name)
|
||||
if (log('Set labels', labels, context.eventName == 'pull_request'))
|
||||
return;
|
||||
|
||||
await github.rest.issues.setLabels({
|
||||
...context.repo,
|
||||
issue_number,
|
||||
labels
|
||||
})
|
||||
} catch (cause) {
|
||||
throw new Error(`Labeling #${item.number} failed.`, { cause })
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (context.payload.pull_request) {
|
||||
await handle(context.payload.pull_request)
|
||||
} else {
|
||||
const lastRun = (await github.rest.actions.listWorkflowRuns({
|
||||
...context.repo,
|
||||
workflow_id: 'labels.yml',
|
||||
event: 'schedule',
|
||||
status: 'success',
|
||||
exclude_pull_requests: true,
|
||||
per_page: 1
|
||||
})).data.workflow_runs[0]
|
||||
|
||||
// Go back as far as the last successful run of this workflow to make sure
|
||||
// we are not leaving anyone behind on GHA failures.
|
||||
// Defaults to go back 1 hour on the first run.
|
||||
const cutoff = new Date(lastRun?.created_at ?? new Date().getTime() - 1 * 60 * 60 * 1000)
|
||||
core.info('cutoff timestamp: ' + cutoff.toISOString())
|
||||
|
||||
const updatedItems = await github.paginate(
|
||||
github.rest.search.issuesAndPullRequests,
|
||||
{
|
||||
q: [
|
||||
`repo:"${process.env.GITHUB_REPOSITORY}"`,
|
||||
'is:open',
|
||||
`updated:>=${cutoff.toISOString()}`
|
||||
].join(' AND '),
|
||||
// TODO: Remove in 2025-10, when it becomes the default.
|
||||
advanced_search: true
|
||||
}
|
||||
)
|
||||
|
||||
let cursor
|
||||
|
||||
// No workflow run available the first time.
|
||||
if (lastRun) {
|
||||
// The cursor to iterate through the full list of issues and pull requests
|
||||
// is passed between jobs as an artifact.
|
||||
const artifact = (await github.rest.actions.listWorkflowRunArtifacts({
|
||||
...context.repo,
|
||||
run_id: lastRun.id,
|
||||
name: 'pagination-cursor'
|
||||
})).data.artifacts[0]
|
||||
|
||||
// If the artifact is not available, the next iteration starts at the beginning.
|
||||
if (artifact) {
|
||||
stats.artifacts++
|
||||
|
||||
const { downloadPath } = await artifactClient.downloadArtifact(artifact.id, {
|
||||
findBy: {
|
||||
repositoryName: context.repo.repo,
|
||||
repositoryOwner: context.repo.owner,
|
||||
token: core.getInput('github-token')
|
||||
},
|
||||
expectedHash: artifact.digest
|
||||
})
|
||||
|
||||
cursor = await readFile(path.resolve(downloadPath, 'cursor'), 'utf-8')
|
||||
}
|
||||
}
|
||||
|
||||
// From GitHub's API docs:
|
||||
// GitHub's REST API considers every pull request an issue, but not every issue is a pull request.
|
||||
// For this reason, "Issues" endpoints may return both issues and pull requests in the response.
|
||||
// You can identify pull requests by the pull_request key.
|
||||
const allItems = await github.rest.issues.listForRepo({
|
||||
...context.repo,
|
||||
state: 'open',
|
||||
sort: 'created',
|
||||
direction: 'asc',
|
||||
per_page: 100,
|
||||
after: cursor
|
||||
})
|
||||
|
||||
// Regex taken and comment adjusted from:
|
||||
// https://github.com/octokit/plugin-paginate-rest.js/blob/8e5da25f975d2f31dda6b8b588d71f2c768a8df2/src/iterator.ts#L36-L41
|
||||
// `allItems.headers.link` format:
|
||||
// <https://api.github.com/repositories/4542716/issues?page=3&per_page=100&after=Y3Vyc29yOnYyOpLPAAABl8qNnYDOvnSJxA%3D%3D>; rel="next",
|
||||
// <https://api.github.com/repositories/4542716/issues?page=1&per_page=100&before=Y3Vyc29yOnYyOpLPAAABl8xFV9DOvoouJg%3D%3D>; rel="prev"
|
||||
// Sets `next` to undefined if "next" URL is not present or `link` header is not set.
|
||||
const next = ((allItems.headers.link ?? '').match(/<([^<>]+)>;\s*rel="next"/) ?? [])[1]
|
||||
if (next) {
|
||||
cursor = new URL(next).searchParams.get('after')
|
||||
const uploadPath = path.resolve('cursor')
|
||||
await writeFile(uploadPath, cursor, 'utf-8')
|
||||
// No stats.artifacts++, because this does not allow passing a custom token.
|
||||
// Thus, the upload will not happen with the app token, but the default github.token.
|
||||
await artifactClient.uploadArtifact(
|
||||
'pagination-cursor',
|
||||
[uploadPath],
|
||||
path.resolve('.'),
|
||||
{
|
||||
retentionDays: 1
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Some items might be in both search results, so filtering out duplicates as well.
|
||||
const items = [].concat(updatedItems, allItems.data)
|
||||
.filter((thisItem, idx, arr) => idx == arr.findIndex(firstItem => firstItem.number == thisItem.number))
|
||||
|
||||
;(await Promise.allSettled(items.map(handle)))
|
||||
.filter(({ status }) => status == 'rejected')
|
||||
.map(({ reason }) => core.setFailed(`${reason.message}\n${reason.cause.stack}`))
|
||||
|
||||
core.notice(`Processed ${stats.prs} PRs, ${stats.issues} Issues, made ${stats.requests + stats.artifacts} API requests and downloaded ${stats.artifacts} artifacts.`)
|
||||
}
|
||||
} finally {
|
||||
clearInterval(reservoirUpdater)
|
||||
}
|
||||
|
||||
- name: Log current API rate limits
|
||||
env:
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# TODO: Move to <top-level>/.editorconfig, once ci/.editorconfig has made its way through staging.
|
||||
[*.cjs]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
@@ -0,0 +1 @@
|
||||
package-lock-only = true
|
||||
@@ -0,0 +1,4 @@
|
||||
To test the labeler locally:
|
||||
- Provide `gh` on `PATH` and make sure it's authenticated.
|
||||
- Enter `nix-shell` in `./ci/labels`.
|
||||
- Run `./run.js OWNER REPO`, where OWNER is your username or "NixOS" and REPO the name of your fork or "nixpkgs".
|
||||
@@ -0,0 +1,463 @@
|
||||
module.exports = async function ({ github, context, core, dry }) {
|
||||
const Bottleneck = require('bottleneck')
|
||||
const path = require('node:path')
|
||||
const { DefaultArtifactClient } = require('@actions/artifact')
|
||||
const { readFile, writeFile } = require('node:fs/promises')
|
||||
|
||||
const artifactClient = new DefaultArtifactClient()
|
||||
|
||||
const stats = {
|
||||
issues: 0,
|
||||
prs: 0,
|
||||
requests: 0,
|
||||
artifacts: 0,
|
||||
}
|
||||
|
||||
// Rate-Limiting and Throttling, see for details:
|
||||
// https://github.com/octokit/octokit.js/issues/1069#throttling
|
||||
// https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api
|
||||
const allLimits = new Bottleneck({
|
||||
// Avoid concurrent requests
|
||||
maxConcurrent: 1,
|
||||
// Will be updated with first `updateReservoir()` call below.
|
||||
reservoir: 0,
|
||||
})
|
||||
// Pause between mutative requests
|
||||
const writeLimits = new Bottleneck({ minTime: 1000 }).chain(allLimits)
|
||||
github.hook.wrap('request', async (request, options) => {
|
||||
// Requests to the /rate_limit endpoint do not count against the rate limit.
|
||||
if (options.url == '/rate_limit') return request(options)
|
||||
// Search requests are in a different resource group, which allows 30 requests / minute.
|
||||
// We do less than a handful each run, so not implementing throttling for now.
|
||||
if (options.url.startsWith('/search/')) return request(options)
|
||||
stats.requests++
|
||||
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(options.method))
|
||||
return writeLimits.schedule(request.bind(null, options))
|
||||
else return allLimits.schedule(request.bind(null, options))
|
||||
})
|
||||
|
||||
async function updateReservoir() {
|
||||
let response
|
||||
try {
|
||||
response = await github.rest.rateLimit.get()
|
||||
} catch (err) {
|
||||
core.error(`Failed updating reservoir:\n${err}`)
|
||||
// Keep retrying on failed rate limit requests instead of exiting the script early.
|
||||
return
|
||||
}
|
||||
// Always keep 1000 spare requests for other jobs to do their regular duty.
|
||||
// They normally use below 100, so 1000 is *plenty* of room to work with.
|
||||
const reservoir = Math.max(0, response.data.resources.core.remaining - 1000)
|
||||
core.info(`Updating reservoir to: ${reservoir}`)
|
||||
allLimits.updateSettings({ reservoir })
|
||||
}
|
||||
await updateReservoir()
|
||||
// Update remaining requests every minute to account for other jobs running in parallel.
|
||||
const reservoirUpdater = setInterval(updateReservoir, 60 * 1000)
|
||||
|
||||
async function handlePullRequest(item) {
|
||||
const log = (k, v) => core.info(`PR #${item.number} - ${k}: ${v}`)
|
||||
|
||||
const pull_number = item.number
|
||||
|
||||
// This API request is important for the merge-conflict label, because it triggers the
|
||||
// creation of a new test merge commit. This is needed to actually determine the state of a PR.
|
||||
const pull_request = (
|
||||
await github.rest.pulls.get({
|
||||
...context.repo,
|
||||
pull_number,
|
||||
})
|
||||
).data
|
||||
|
||||
const reviews = await github.paginate(github.rest.pulls.listReviews, {
|
||||
...context.repo,
|
||||
pull_number,
|
||||
})
|
||||
|
||||
const approvals = new Set(
|
||||
reviews
|
||||
.filter((review) => review.state == 'APPROVED')
|
||||
.map((review) => review.user?.id),
|
||||
)
|
||||
|
||||
// After creation of a Pull Request, `merge_commit_sha` will be null initially:
|
||||
// The very first merge commit will only be calculated after a little while.
|
||||
// To avoid labeling the PR as conflicted before that, we wait a few minutes.
|
||||
// This is intentionally less than the time that Eval takes, so that the label job
|
||||
// running after Eval can indeed label the PR as conflicted if that is the case.
|
||||
const merge_commit_sha_valid =
|
||||
new Date() - new Date(pull_request.created_at) > 3 * 60 * 1000
|
||||
|
||||
const prLabels = {
|
||||
// We intentionally don't use the mergeable or mergeable_state attributes.
|
||||
// Those have an intermediate state while the test merge commit is created.
|
||||
// This doesn't work well for us, because we might have just triggered another
|
||||
// test merge commit creation by request the pull request via API at the start
|
||||
// of this function.
|
||||
// The attribute merge_commit_sha keeps the old value of null or the hash *until*
|
||||
// the new test merge commit has either successfully been created or failed so.
|
||||
// This essentially means we are updating the merge conflict label in two steps:
|
||||
// On the first pass of the day, we just fetch the pull request, which triggers
|
||||
// the creation. At this stage, the label is likely not updated, yet.
|
||||
// The second pass will then read the result from the first pass and set the label.
|
||||
'2.status: merge conflict':
|
||||
merge_commit_sha_valid && !pull_request.merge_commit_sha,
|
||||
'12.approvals: 1': approvals.size == 1,
|
||||
'12.approvals: 2': approvals.size == 2,
|
||||
'12.approvals: 3+': approvals.size >= 3,
|
||||
'12.first-time contribution': [
|
||||
'NONE',
|
||||
'FIRST_TIMER',
|
||||
'FIRST_TIME_CONTRIBUTOR',
|
||||
].includes(pull_request.author_association),
|
||||
}
|
||||
|
||||
const { id: run_id, conclusion } =
|
||||
(
|
||||
await github.rest.actions.listWorkflowRuns({
|
||||
...context.repo,
|
||||
workflow_id: 'pr.yml',
|
||||
event: 'pull_request_target',
|
||||
exclude_pull_requests: true,
|
||||
head_sha: pull_request.head.sha,
|
||||
})
|
||||
).data.workflow_runs[0] ??
|
||||
// TODO: Remove this after 2025-09-17, at which point all eval.yml artifacts will have expired.
|
||||
(
|
||||
await github.rest.actions.listWorkflowRuns({
|
||||
...context.repo,
|
||||
// In older PRs, we need eval.yml instead of pr.yml.
|
||||
workflow_id: 'eval.yml',
|
||||
event: 'pull_request_target',
|
||||
status: 'success',
|
||||
exclude_pull_requests: true,
|
||||
head_sha: pull_request.head.sha,
|
||||
})
|
||||
).data.workflow_runs[0] ??
|
||||
{}
|
||||
|
||||
// Newer PRs might not have run Eval to completion, yet.
|
||||
// Older PRs might not have an eval.yml workflow, yet.
|
||||
// In either case we continue without fetching an artifact on a best-effort basis.
|
||||
log('Last eval run', run_id ?? '<n/a>')
|
||||
|
||||
if (conclusion === 'success') {
|
||||
Object.assign(prLabels, {
|
||||
// We only set this label if the latest eval run was successful, because if it was not, it
|
||||
// *could* have requested reviewers. We will let the PR author fix CI first, before "escalating"
|
||||
// this PR to "needs: reviewer".
|
||||
// Since the first Eval run on a PR always sets rebuild labels, the same PR will be "recently
|
||||
// updated" for the next scheduled run. Thus, this label will still be set within a few minutes
|
||||
// after a PR is created, if required.
|
||||
// Note that a "requested reviewer" disappears once they have given a review, so we check
|
||||
// existing reviews, too.
|
||||
'9.needs: reviewer':
|
||||
!pull_request.draft &&
|
||||
pull_request.requested_reviewers.length == 0 &&
|
||||
reviews.length == 0,
|
||||
})
|
||||
}
|
||||
|
||||
const artifact =
|
||||
run_id &&
|
||||
(
|
||||
await github.rest.actions.listWorkflowRunArtifacts({
|
||||
...context.repo,
|
||||
run_id,
|
||||
name: 'comparison',
|
||||
})
|
||||
).data.artifacts[0]
|
||||
|
||||
// Instead of checking the boolean artifact.expired, we will give us a minute to
|
||||
// actually download the artifact in the next step and avoid that race condition.
|
||||
// Older PRs, where the workflow run was already eval.yml, but the artifact was not
|
||||
// called "comparison", yet, will skip the download.
|
||||
const expired =
|
||||
!artifact ||
|
||||
new Date(artifact?.expires_at ?? 0) <
|
||||
new Date(new Date().getTime() + 60 * 1000)
|
||||
log('Artifact expires at', artifact?.expires_at ?? '<n/a>')
|
||||
if (!expired) {
|
||||
stats.artifacts++
|
||||
|
||||
await artifactClient.downloadArtifact(artifact.id, {
|
||||
findBy: {
|
||||
repositoryName: context.repo.repo,
|
||||
repositoryOwner: context.repo.owner,
|
||||
token: core.getInput('github-token'),
|
||||
},
|
||||
path: path.resolve(pull_number.toString()),
|
||||
expectedHash: artifact.digest,
|
||||
})
|
||||
|
||||
const maintainers = new Set(
|
||||
Object.keys(
|
||||
JSON.parse(
|
||||
await readFile(`${pull_number}/maintainers.json`, 'utf-8'),
|
||||
),
|
||||
).map((m) => Number.parseInt(m, 10)),
|
||||
)
|
||||
|
||||
const evalLabels = JSON.parse(
|
||||
await readFile(`${pull_number}/changed-paths.json`, 'utf-8'),
|
||||
).labels
|
||||
|
||||
Object.assign(
|
||||
prLabels,
|
||||
// Ignore `evalLabels` if it's an array.
|
||||
// This can happen for older eval runs, before we switched to objects.
|
||||
// The old eval labels would have been set by the eval run,
|
||||
// so now they'll be present in `before`.
|
||||
// TODO: Simplify once old eval results have expired (~2025-10)
|
||||
Array.isArray(evalLabels) ? undefined : evalLabels,
|
||||
{
|
||||
'12.approved-by: package-maintainer': Array.from(maintainers).some(
|
||||
(m) => approvals.has(m),
|
||||
),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return prLabels
|
||||
}
|
||||
|
||||
async function handle(item) {
|
||||
try {
|
||||
const log = (k, v, skip) => {
|
||||
core.info(`#${item.number} - ${k}: ${v}` + (skip ? ' (skipped)' : ''))
|
||||
return skip
|
||||
}
|
||||
|
||||
log('Last updated at', item.updated_at)
|
||||
log('URL', item.html_url)
|
||||
|
||||
const issue_number = item.number
|
||||
|
||||
const itemLabels = {}
|
||||
|
||||
if (item.pull_request) {
|
||||
stats.prs++
|
||||
Object.assign(itemLabels, await handlePullRequest(item))
|
||||
} else {
|
||||
stats.issues++
|
||||
}
|
||||
|
||||
const latest_event_at = new Date(
|
||||
(
|
||||
await github.paginate(github.rest.issues.listEventsForTimeline, {
|
||||
...context.repo,
|
||||
issue_number,
|
||||
per_page: 100,
|
||||
})
|
||||
)
|
||||
.filter(({ event }) =>
|
||||
[
|
||||
// These events are hand-picked from:
|
||||
// https://docs.github.com/en/rest/using-the-rest-api/issue-event-types?apiVersion=2022-11-28
|
||||
// Each of those causes a PR/issue to *not* be considered as stale anymore.
|
||||
// Most of these use created_at.
|
||||
'assigned',
|
||||
'commented', // uses updated_at, because that could be > created_at
|
||||
'committed', // uses committer.date
|
||||
'head_ref_force_pushed',
|
||||
'milestoned',
|
||||
'pinned',
|
||||
'ready_for_review',
|
||||
'renamed',
|
||||
'reopened',
|
||||
'review_dismissed',
|
||||
'review_requested',
|
||||
'reviewed', // uses submitted_at
|
||||
'unlocked',
|
||||
'unmarked_as_duplicate',
|
||||
].includes(event),
|
||||
)
|
||||
.map(
|
||||
({ created_at, updated_at, committer, submitted_at }) =>
|
||||
new Date(
|
||||
updated_at ?? created_at ?? submitted_at ?? committer.date,
|
||||
),
|
||||
)
|
||||
// Reverse sort by date value. The default sort() sorts by string representation, which is bad for dates.
|
||||
.sort((a, b) => b - a)
|
||||
.at(0) ?? item.created_at,
|
||||
)
|
||||
log('latest_event_at', latest_event_at.toISOString())
|
||||
|
||||
const stale_at = new Date(new Date().setDate(new Date().getDate() - 180))
|
||||
|
||||
// Create a map (Label -> Boolean) of all currently set labels.
|
||||
// Each label is set to True and can be disabled later.
|
||||
const before = Object.fromEntries(
|
||||
(
|
||||
await github.paginate(github.rest.issues.listLabelsOnIssue, {
|
||||
...context.repo,
|
||||
issue_number,
|
||||
})
|
||||
).map(({ name }) => [name, true]),
|
||||
)
|
||||
|
||||
Object.assign(itemLabels, {
|
||||
'2.status: stale':
|
||||
!before['1.severity: security'] && latest_event_at < stale_at,
|
||||
})
|
||||
|
||||
const after = Object.assign({}, before, itemLabels)
|
||||
|
||||
// No need for an API request, if all labels are the same.
|
||||
const hasChanges = Object.keys(after).some(
|
||||
(name) => (before[name] ?? false) != after[name],
|
||||
)
|
||||
if (log('Has changes', hasChanges, !hasChanges)) return
|
||||
|
||||
// Skipping labeling on a pull_request event, because we have no privileges.
|
||||
const labels = Object.entries(after)
|
||||
.filter(([, value]) => value)
|
||||
.map(([name]) => name)
|
||||
if (log('Set labels', labels, dry)) return
|
||||
|
||||
await github.rest.issues.setLabels({
|
||||
...context.repo,
|
||||
issue_number,
|
||||
labels,
|
||||
})
|
||||
} catch (cause) {
|
||||
throw new Error(`Labeling #${item.number} failed.`, { cause })
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (context.payload.pull_request) {
|
||||
await handle(context.payload.pull_request)
|
||||
} else {
|
||||
const lastRun = (
|
||||
await github.rest.actions.listWorkflowRuns({
|
||||
...context.repo,
|
||||
workflow_id: 'labels.yml',
|
||||
event: 'schedule',
|
||||
status: 'success',
|
||||
exclude_pull_requests: true,
|
||||
per_page: 1,
|
||||
})
|
||||
).data.workflow_runs[0]
|
||||
|
||||
// Go back as far as the last successful run of this workflow to make sure
|
||||
// we are not leaving anyone behind on GHA failures.
|
||||
// Defaults to go back 1 hour on the first run.
|
||||
const cutoff = new Date(
|
||||
lastRun?.created_at ?? new Date().getTime() - 1 * 60 * 60 * 1000,
|
||||
)
|
||||
core.info('cutoff timestamp: ' + cutoff.toISOString())
|
||||
|
||||
const updatedItems = await github.paginate(
|
||||
github.rest.search.issuesAndPullRequests,
|
||||
{
|
||||
q: [
|
||||
`repo:"${context.repo.owner}/${context.repo.repo}"`,
|
||||
'is:open',
|
||||
`updated:>=${cutoff.toISOString()}`,
|
||||
].join(' AND '),
|
||||
// TODO: Remove in 2025-10, when it becomes the default.
|
||||
advanced_search: true,
|
||||
},
|
||||
)
|
||||
|
||||
let cursor
|
||||
|
||||
// No workflow run available the first time.
|
||||
if (lastRun) {
|
||||
// The cursor to iterate through the full list of issues and pull requests
|
||||
// is passed between jobs as an artifact.
|
||||
const artifact = (
|
||||
await github.rest.actions.listWorkflowRunArtifacts({
|
||||
...context.repo,
|
||||
run_id: lastRun.id,
|
||||
name: 'pagination-cursor',
|
||||
})
|
||||
).data.artifacts[0]
|
||||
|
||||
// If the artifact is not available, the next iteration starts at the beginning.
|
||||
if (artifact) {
|
||||
stats.artifacts++
|
||||
|
||||
const { downloadPath } = await artifactClient.downloadArtifact(
|
||||
artifact.id,
|
||||
{
|
||||
findBy: {
|
||||
repositoryName: context.repo.repo,
|
||||
repositoryOwner: context.repo.owner,
|
||||
token: core.getInput('github-token'),
|
||||
},
|
||||
expectedHash: artifact.digest,
|
||||
},
|
||||
)
|
||||
|
||||
cursor = await readFile(path.resolve(downloadPath, 'cursor'), 'utf-8')
|
||||
}
|
||||
}
|
||||
|
||||
// From GitHub's API docs:
|
||||
// GitHub's REST API considers every pull request an issue, but not every issue is a pull request.
|
||||
// For this reason, "Issues" endpoints may return both issues and pull requests in the response.
|
||||
// You can identify pull requests by the pull_request key.
|
||||
const allItems = await github.rest.issues.listForRepo({
|
||||
...context.repo,
|
||||
state: 'open',
|
||||
sort: 'created',
|
||||
direction: 'asc',
|
||||
per_page: 100,
|
||||
after: cursor,
|
||||
})
|
||||
|
||||
// Regex taken and comment adjusted from:
|
||||
// https://github.com/octokit/plugin-paginate-rest.js/blob/8e5da25f975d2f31dda6b8b588d71f2c768a8df2/src/iterator.ts#L36-L41
|
||||
// `allItems.headers.link` format:
|
||||
// <https://api.github.com/repositories/4542716/issues?page=3&per_page=100&after=Y3Vyc29yOnYyOpLPAAABl8qNnYDOvnSJxA%3D%3D>; rel="next",
|
||||
// <https://api.github.com/repositories/4542716/issues?page=1&per_page=100&before=Y3Vyc29yOnYyOpLPAAABl8xFV9DOvoouJg%3D%3D>; rel="prev"
|
||||
// Sets `next` to undefined if "next" URL is not present or `link` header is not set.
|
||||
const next = ((allItems.headers.link ?? '').match(
|
||||
/<([^<>]+)>;\s*rel="next"/,
|
||||
) ?? [])[1]
|
||||
if (next) {
|
||||
cursor = new URL(next).searchParams.get('after')
|
||||
const uploadPath = path.resolve('cursor')
|
||||
await writeFile(uploadPath, cursor, 'utf-8')
|
||||
if (dry) {
|
||||
core.info(`pagination-cursor: ${cursor} (upload skipped)`)
|
||||
} else {
|
||||
// No stats.artifacts++, because this does not allow passing a custom token.
|
||||
// Thus, the upload will not happen with the app token, but the default github.token.
|
||||
await artifactClient.uploadArtifact(
|
||||
'pagination-cursor',
|
||||
[uploadPath],
|
||||
path.resolve('.'),
|
||||
{
|
||||
retentionDays: 1,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Some items might be in both search results, so filtering out duplicates as well.
|
||||
const items = []
|
||||
.concat(updatedItems, allItems.data)
|
||||
.filter(
|
||||
(thisItem, idx, arr) =>
|
||||
idx ==
|
||||
arr.findIndex((firstItem) => firstItem.number == thisItem.number),
|
||||
)
|
||||
|
||||
;(await Promise.allSettled(items.map(handle)))
|
||||
.filter(({ status }) => status == 'rejected')
|
||||
.map(({ reason }) =>
|
||||
core.setFailed(`${reason.message}\n${reason.cause.stack}`),
|
||||
)
|
||||
|
||||
core.notice(
|
||||
`Processed ${stats.prs} PRs, ${stats.issues} Issues, made ${stats.requests + stats.artifacts} API requests and downloaded ${stats.artifacts} artifacts.`,
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
clearInterval(reservoirUpdater)
|
||||
}
|
||||
}
|
||||
Generated
+1897
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@actions/artifact": "2.3.2",
|
||||
"@actions/github": "6.0.1",
|
||||
"bottleneck": "2.19.5"
|
||||
}
|
||||
}
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env node
|
||||
import { execSync } from 'node:child_process'
|
||||
import { mkdtempSync, rmSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { getOctokit } from '@actions/github'
|
||||
import labels from './labels.cjs'
|
||||
|
||||
if (process.argv.length !== 4)
|
||||
throw new Error('Call this with exactly two arguments: ./run.js OWNER REPO')
|
||||
const [, , owner, repo] = process.argv
|
||||
|
||||
const token = execSync('gh auth token', { encoding: 'utf-8' }).trim()
|
||||
|
||||
const tmp = mkdtempSync(join(tmpdir(), 'labels-'))
|
||||
try {
|
||||
process.env.GITHUB_WORKSPACE = tmp
|
||||
process.chdir(tmp)
|
||||
|
||||
await labels({
|
||||
github: getOctokit(token),
|
||||
context: {
|
||||
payload: {},
|
||||
repo: {
|
||||
owner,
|
||||
repo,
|
||||
},
|
||||
},
|
||||
core: {
|
||||
getInput() {
|
||||
return token
|
||||
},
|
||||
error: console.error,
|
||||
info: console.log,
|
||||
notice: console.log,
|
||||
setFailed(msg) {
|
||||
console.error(msg)
|
||||
process.exitCode = 1
|
||||
},
|
||||
},
|
||||
dry: true,
|
||||
})
|
||||
} finally {
|
||||
rmSync(tmp, { recursive: true })
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
system ? builtins.currentSystem,
|
||||
pkgs ? (import ../. { inherit system; }).pkgs,
|
||||
}:
|
||||
|
||||
pkgs.callPackage (
|
||||
{
|
||||
mkShell,
|
||||
importNpmLock,
|
||||
nodejs,
|
||||
}:
|
||||
mkShell {
|
||||
packages = [
|
||||
importNpmLock.hooks.linkNodeModulesHook
|
||||
nodejs
|
||||
];
|
||||
|
||||
npmDeps = importNpmLock.buildNodeModules {
|
||||
npmRoot = ./.;
|
||||
inherit nodejs;
|
||||
};
|
||||
}
|
||||
) { }
|
||||
@@ -840,7 +840,7 @@ Used with CVS. Expects `cvsRoot`, `tag`, and `hash`.
|
||||
|
||||
## `fetchhg` {#fetchhg}
|
||||
|
||||
Used with Mercurial. Expects `url`, `rev`, and `hash`.
|
||||
Used with Mercurial. Expects `url`, `rev`, `hash`, overridable with [`<pkg>.overrideAttrs`](#sec-pkg-overrideAttrs).
|
||||
|
||||
A number of fetcher functions wrap part of `fetchurl` and `fetchzip`. They are mainly convenience functions intended for commonly used destinations of source code in Nixpkgs. These wrapper fetchers are listed below.
|
||||
|
||||
|
||||
+3
-1
@@ -42,7 +42,9 @@ let
|
||||
# Filter out version control software files/directories
|
||||
(
|
||||
baseName == ".git"
|
||||
|| type == "directory" && (baseName == ".svn" || baseName == "CVS" || baseName == ".hg")
|
||||
||
|
||||
type == "directory"
|
||||
&& (baseName == ".svn" || baseName == "CVS" || baseName == ".hg" || baseName == ".jj")
|
||||
)
|
||||
||
|
||||
# Filter out editor backup / swap files.
|
||||
|
||||
@@ -4108,6 +4108,12 @@
|
||||
name = "Cameron Smith";
|
||||
keys = [ { fingerprint = "3F14 C258 856E 88AE E0F9 661E FF04 3B36 8811 DD1C"; } ];
|
||||
};
|
||||
cameronyule = {
|
||||
email = "cameron@cameronyule.com";
|
||||
github = "cameronyule";
|
||||
githubId = 5451;
|
||||
name = "Cameron Yule";
|
||||
};
|
||||
camillemndn = {
|
||||
email = "camillemondon@free.fr";
|
||||
github = "camillemndn";
|
||||
@@ -6242,6 +6248,17 @@
|
||||
github = "Dettorer";
|
||||
githubId = 2761682;
|
||||
};
|
||||
deudz = {
|
||||
name = "Danilo Soares";
|
||||
email = "deudzdev@gmail.com";
|
||||
github = "deudz";
|
||||
githubId = 77695632;
|
||||
keys = [
|
||||
{
|
||||
fingerprint = "42B9 5F7C 4FC2 CA13 FD4E 86B6 F0D8 B7CE 0B7E C148";
|
||||
}
|
||||
];
|
||||
};
|
||||
developer-guy = {
|
||||
name = "Batuhan Apaydın";
|
||||
email = "developerguyn@gmail.com";
|
||||
|
||||
@@ -478,7 +478,7 @@ with lib.maintainers;
|
||||
willcohen
|
||||
];
|
||||
githubTeams = [ "geospatial" ];
|
||||
scope = "Maintain geospatial packages.";
|
||||
scope = "Maintain geospatial, remote sensing and OpenStreetMap software.";
|
||||
shortName = "Geospatial";
|
||||
enableFeatureFreezePing = true;
|
||||
};
|
||||
|
||||
@@ -53,7 +53,7 @@ def get_tmp_dir() -> Path:
|
||||
|
||||
|
||||
def pythonize_name(name: str) -> str:
|
||||
return re.sub(r"^[^A-z_]|[^A-z0-9_]", "_", name)
|
||||
return re.sub(r"^[^A-Za-z_]|[^A-Za-z0-9_]", "_", name)
|
||||
|
||||
|
||||
class Driver:
|
||||
|
||||
@@ -90,7 +90,7 @@ in
|
||||
};
|
||||
|
||||
dns = {
|
||||
server = mkOption {
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1:53";
|
||||
description = ''
|
||||
@@ -173,7 +173,7 @@ in
|
||||
description = "Postfix DANE/MTA-STS TLS policy socketmap service";
|
||||
documentation = [ "https://github.com/Zuplu/postfix-tlspol" ];
|
||||
|
||||
reloadTriggers = [ configFile ];
|
||||
restartTriggers = [ configFile ];
|
||||
|
||||
# https://github.com/Zuplu/postfix-tlspol/blob/main/init/postfix-tlspol.service
|
||||
serviceConfig = {
|
||||
|
||||
@@ -24,8 +24,6 @@ let
|
||||
BOOTSNAP_CACHE_DIR = "/var/cache/mastodon/precompile";
|
||||
LD_PRELOAD = "${pkgs.jemalloc}/lib/libjemalloc.so";
|
||||
|
||||
MASTODON_USE_LIBVIPS = "true";
|
||||
|
||||
# Concurrency mastodon-web
|
||||
WEB_CONCURRENCY = toString cfg.webProcesses;
|
||||
MAX_THREADS = toString cfg.webThreads;
|
||||
@@ -196,7 +194,6 @@ let
|
||||
path = with pkgs; [
|
||||
ffmpeg-headless
|
||||
file
|
||||
imagemagick
|
||||
];
|
||||
}
|
||||
)
|
||||
@@ -257,6 +254,11 @@ in
|
||||
"mastodon"
|
||||
"streamingPort"
|
||||
] "Mastodon currently doesn't support streaming via TCP ports. Please open a PR if you need this.")
|
||||
(lib.mkRemovedOptionModule [
|
||||
"services"
|
||||
"mastodon"
|
||||
"otpSecretFile"
|
||||
] "The OTP_SECRET option was removed from Mastodon in version 4.4.0")
|
||||
];
|
||||
|
||||
options = {
|
||||
@@ -490,19 +492,6 @@ in
|
||||
type = lib.types.str;
|
||||
};
|
||||
|
||||
otpSecretFile = lib.mkOption {
|
||||
description = ''
|
||||
Path to file containing the OTP secret.
|
||||
A new OTP secret can be generated by running:
|
||||
|
||||
`nix build -f '<nixpkgs>' mastodon; cd result; bin/bundle exec rails secret`
|
||||
|
||||
If this file does not exist, it will be created with a new OTP secret.
|
||||
'';
|
||||
default = "/var/lib/mastodon/secrets/otp-secret";
|
||||
type = lib.types.str;
|
||||
};
|
||||
|
||||
trustedProxy = lib.mkOption {
|
||||
description = ''
|
||||
You need to set it to the IP from which your reverse proxy sends requests to Mastodon's web process,
|
||||
@@ -892,10 +881,6 @@ in
|
||||
mkdir -p $(dirname ${cfg.secretKeyBaseFile})
|
||||
bin/bundle exec rails secret > ${cfg.secretKeyBaseFile}
|
||||
fi
|
||||
if ! test -f ${cfg.otpSecretFile}; then
|
||||
mkdir -p $(dirname ${cfg.otpSecretFile})
|
||||
bin/bundle exec rails secret > ${cfg.otpSecretFile}
|
||||
fi
|
||||
if ! test -f ${cfg.vapidPrivateKeyFile}; then
|
||||
mkdir -p $(dirname ${cfg.vapidPrivateKeyFile}) $(dirname ${cfg.vapidPublicKeyFile})
|
||||
keypair=$(bin/rake webpush:generate_keys)
|
||||
@@ -908,7 +893,6 @@ in
|
||||
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT="$(cat ${cfg.activeRecordEncryptionKeyDerivationSaltFile})"
|
||||
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY="$(cat ${cfg.activeRecordEncryptionPrimaryKeyFile})"
|
||||
SECRET_KEY_BASE="$(cat ${cfg.secretKeyBaseFile})"
|
||||
OTP_SECRET="$(cat ${cfg.otpSecretFile})"
|
||||
VAPID_PRIVATE_KEY="$(cat ${cfg.vapidPrivateKeyFile})"
|
||||
VAPID_PUBLIC_KEY="$(cat ${cfg.vapidPublicKeyFile})"
|
||||
''
|
||||
|
||||
+10
-10
@@ -445,7 +445,7 @@ in
|
||||
fscrypt = runTest ./fscrypt.nix;
|
||||
fastnetmon-advanced = runTest ./fastnetmon-advanced.nix;
|
||||
lauti = runTest ./lauti.nix;
|
||||
easytier = handleTest ./easytier.nix { };
|
||||
easytier = runTest ./easytier.nix;
|
||||
ejabberd = runTest ./xmpp/ejabberd.nix;
|
||||
elk = handleTestOn [ "x86_64-linux" ] ./elk.nix { };
|
||||
emacs-daemon = runTest ./emacs-daemon.nix;
|
||||
@@ -613,7 +613,7 @@ in
|
||||
gokapi = runTest ./gokapi.nix;
|
||||
gollum = runTest ./gollum.nix;
|
||||
gonic = runTest ./gonic.nix;
|
||||
google-oslogin = handleTest ./google-oslogin { };
|
||||
google-oslogin = runTest ./google-oslogin;
|
||||
gopro-tool = runTest ./gopro-tool.nix;
|
||||
goss = runTest ./goss.nix;
|
||||
gotenberg = runTest ./gotenberg.nix;
|
||||
@@ -853,7 +853,7 @@ in
|
||||
mautrix-meta-sqlite = runTest ./matrix/mautrix-meta-sqlite.nix;
|
||||
mealie = runTest ./mealie.nix;
|
||||
mediamtx = runTest ./mediamtx.nix;
|
||||
mediatomb = handleTest ./mediatomb.nix { };
|
||||
mediatomb = runTest ./mediatomb.nix;
|
||||
mediawiki = handleTest ./mediawiki.nix { };
|
||||
meilisearch = runTest ./meilisearch.nix;
|
||||
memcached = runTest ./memcached.nix;
|
||||
@@ -901,11 +901,11 @@ in
|
||||
mumble = runTest ./mumble.nix;
|
||||
# Fails on aarch64-linux at the PDF creation step - need to debug this on an
|
||||
# aarch64 machine..
|
||||
musescore = handleTestOn [ "x86_64-linux" ] ./musescore.nix { };
|
||||
musescore = runTestOn [ "x86_64-linux" ] ./musescore.nix;
|
||||
music-assistant = runTest ./music-assistant.nix;
|
||||
munin = runTest ./munin.nix;
|
||||
mutableUsers = runTest ./mutable-users.nix;
|
||||
mycelium = handleTest ./mycelium { };
|
||||
mycelium = runTest ./mycelium;
|
||||
mympd = runTest ./mympd.nix;
|
||||
mysql = handleTest ./mysql/mysql.nix { };
|
||||
mysql-autobackup = handleTest ./mysql/mysql-autobackup.nix { };
|
||||
@@ -935,7 +935,7 @@ in
|
||||
};
|
||||
ndppd = runTest ./ndppd.nix;
|
||||
nebula = runTest ./nebula.nix;
|
||||
neo4j = handleTest ./neo4j.nix { };
|
||||
neo4j = runTest ./neo4j.nix;
|
||||
netbird = runTest ./netbird.nix;
|
||||
netdata = runTest ./netdata.nix;
|
||||
nimdow = runTest ./nimdow.nix;
|
||||
@@ -1261,11 +1261,11 @@ in
|
||||
sanoid = runTest ./sanoid.nix;
|
||||
saunafs = runTest ./saunafs.nix;
|
||||
scaphandre = handleTest ./scaphandre.nix { };
|
||||
schleuder = handleTest ./schleuder.nix { };
|
||||
schleuder = runTest ./schleuder.nix;
|
||||
scion-freestanding-deployment = handleTest ./scion/freestanding-deployment { };
|
||||
scrutiny = runTest ./scrutiny.nix;
|
||||
scx = runTest ./scx/default.nix;
|
||||
sddm = handleTest ./sddm.nix { };
|
||||
sddm = import ./sddm.nix { inherit runTest; };
|
||||
sdl3 = runTest ./sdl3.nix;
|
||||
seafile = runTest ./seafile.nix;
|
||||
searx = runTest ./searx.nix;
|
||||
@@ -1454,7 +1454,7 @@ in
|
||||
tracee = handleTestOn [ "x86_64-linux" ] ./tracee.nix { };
|
||||
trezord = runTest ./trezord.nix;
|
||||
trickster = runTest ./trickster.nix;
|
||||
trilium-server = handleTestOn [ "x86_64-linux" ] ./trilium-server.nix { };
|
||||
trilium-server = runTestOn [ "x86_64-linux" ] ./trilium-server.nix;
|
||||
tsm-client-gui = runTest ./tsm-client-gui.nix;
|
||||
ttyd = runTest ./web-servers/ttyd.nix;
|
||||
tt-rss = runTest ./web-apps/tt-rss.nix;
|
||||
@@ -1511,7 +1511,7 @@ in
|
||||
velocity = runTest ./velocity.nix;
|
||||
vengi-tools = runTest ./vengi-tools.nix;
|
||||
victorialogs = runTest ./victorialogs.nix;
|
||||
victoriametrics = handleTest ./victoriametrics { };
|
||||
victoriametrics = import ./victoriametrics { inherit runTest; };
|
||||
vikunja = runTest ./vikunja.nix;
|
||||
virtualbox = handleTestOn [ "x86_64-linux" ] ./virtualbox.nix { };
|
||||
vm-variant = handleTest ./vm-variant.nix { };
|
||||
|
||||
+106
-108
@@ -1,121 +1,119 @@
|
||||
import ./make-test-python.nix (
|
||||
{ lib, ... }:
|
||||
{
|
||||
name = "easytier";
|
||||
meta.maintainers = with lib.maintainers; [ ltrump ];
|
||||
{ lib, ... }:
|
||||
{
|
||||
name = "easytier";
|
||||
meta.maintainers = with lib.maintainers; [ ltrump ];
|
||||
|
||||
nodes =
|
||||
let
|
||||
genPeer =
|
||||
hostConfig: settings:
|
||||
lib.mkMerge [
|
||||
{
|
||||
services.easytier = {
|
||||
enable = true;
|
||||
instances.default = {
|
||||
settings = {
|
||||
network_name = "easytier_test";
|
||||
network_secret = "easytier_test_secret";
|
||||
} // settings;
|
||||
};
|
||||
nodes =
|
||||
let
|
||||
genPeer =
|
||||
hostConfig: settings:
|
||||
lib.mkMerge [
|
||||
{
|
||||
services.easytier = {
|
||||
enable = true;
|
||||
instances.default = {
|
||||
settings = {
|
||||
network_name = "easytier_test";
|
||||
network_secret = "easytier_test_secret";
|
||||
} // settings;
|
||||
};
|
||||
|
||||
networking.useDHCP = false;
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
11010
|
||||
11011
|
||||
];
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
11010
|
||||
11011
|
||||
];
|
||||
}
|
||||
hostConfig
|
||||
];
|
||||
in
|
||||
{
|
||||
relay =
|
||||
genPeer
|
||||
{
|
||||
virtualisation.vlans = [
|
||||
1
|
||||
2
|
||||
];
|
||||
|
||||
networking.interfaces.eth1.ipv4.addresses = [
|
||||
{
|
||||
address = "192.168.1.11";
|
||||
prefixLength = 24;
|
||||
}
|
||||
];
|
||||
|
||||
networking.interfaces.eth2.ipv4.addresses = [
|
||||
{
|
||||
address = "192.168.2.11";
|
||||
prefixLength = 24;
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
ipv4 = "10.144.144.1";
|
||||
listeners = [
|
||||
"tcp://0.0.0.0:11010"
|
||||
"wss://0.0.0.0:11011"
|
||||
];
|
||||
};
|
||||
|
||||
peer1 =
|
||||
genPeer
|
||||
{
|
||||
virtualisation.vlans = [ 1 ];
|
||||
}
|
||||
{
|
||||
ipv4 = "10.144.144.2";
|
||||
peers = [ "tcp://192.168.1.11:11010" ];
|
||||
};
|
||||
networking.useDHCP = false;
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
11010
|
||||
11011
|
||||
];
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
11010
|
||||
11011
|
||||
];
|
||||
}
|
||||
hostConfig
|
||||
];
|
||||
in
|
||||
{
|
||||
relay =
|
||||
genPeer
|
||||
{
|
||||
virtualisation.vlans = [
|
||||
1
|
||||
2
|
||||
];
|
||||
|
||||
peer2 =
|
||||
genPeer
|
||||
{
|
||||
virtualisation.vlans = [ 2 ];
|
||||
}
|
||||
{
|
||||
ipv4 = "10.144.144.3";
|
||||
peers = [ "wss://192.168.2.11:11011" ];
|
||||
};
|
||||
};
|
||||
networking.interfaces.eth1.ipv4.addresses = [
|
||||
{
|
||||
address = "192.168.1.11";
|
||||
prefixLength = 24;
|
||||
}
|
||||
];
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
networking.interfaces.eth2.ipv4.addresses = [
|
||||
{
|
||||
address = "192.168.2.11";
|
||||
prefixLength = 24;
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
ipv4 = "10.144.144.1";
|
||||
listeners = [
|
||||
"tcp://0.0.0.0:11010"
|
||||
"wss://0.0.0.0:11011"
|
||||
];
|
||||
};
|
||||
|
||||
relay.wait_for_unit("easytier-default.service")
|
||||
peer1.wait_for_unit("easytier-default.service")
|
||||
peer2.wait_for_unit("easytier-default.service")
|
||||
peer1 =
|
||||
genPeer
|
||||
{
|
||||
virtualisation.vlans = [ 1 ];
|
||||
}
|
||||
{
|
||||
ipv4 = "10.144.144.2";
|
||||
peers = [ "tcp://192.168.1.11:11010" ];
|
||||
};
|
||||
|
||||
# relay is accessible by the other hosts
|
||||
peer1.succeed("ping -c5 192.168.1.11")
|
||||
peer2.succeed("ping -c5 192.168.2.11")
|
||||
peer2 =
|
||||
genPeer
|
||||
{
|
||||
virtualisation.vlans = [ 2 ];
|
||||
}
|
||||
{
|
||||
ipv4 = "10.144.144.3";
|
||||
peers = [ "wss://192.168.2.11:11011" ];
|
||||
};
|
||||
};
|
||||
|
||||
# The other hosts are in separate vlans
|
||||
peer1.fail("ping -c5 192.168.2.11")
|
||||
peer2.fail("ping -c5 192.168.1.11")
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
# Each host can ping themselves through EasyTier
|
||||
relay.succeed("ping -c5 10.144.144.1")
|
||||
peer1.succeed("ping -c5 10.144.144.2")
|
||||
peer2.succeed("ping -c5 10.144.144.3")
|
||||
relay.wait_for_unit("easytier-default.service")
|
||||
peer1.wait_for_unit("easytier-default.service")
|
||||
peer2.wait_for_unit("easytier-default.service")
|
||||
|
||||
# Relay is accessible by the other hosts through EasyTier
|
||||
peer1.succeed("ping -c5 10.144.144.1")
|
||||
peer2.succeed("ping -c5 10.144.144.1")
|
||||
# relay is accessible by the other hosts
|
||||
peer1.succeed("ping -c5 192.168.1.11")
|
||||
peer2.succeed("ping -c5 192.168.2.11")
|
||||
|
||||
# Relay can access the other hosts through EasyTier
|
||||
relay.succeed("ping -c5 10.144.144.2")
|
||||
relay.succeed("ping -c5 10.144.144.3")
|
||||
# The other hosts are in separate vlans
|
||||
peer1.fail("ping -c5 192.168.2.11")
|
||||
peer2.fail("ping -c5 192.168.1.11")
|
||||
|
||||
# The other hosts in separate vlans can access each other through EasyTier
|
||||
peer1.succeed("ping -c5 10.144.144.3")
|
||||
peer2.succeed("ping -c5 10.144.144.2")
|
||||
'';
|
||||
}
|
||||
)
|
||||
# Each host can ping themselves through EasyTier
|
||||
relay.succeed("ping -c5 10.144.144.1")
|
||||
peer1.succeed("ping -c5 10.144.144.2")
|
||||
peer2.succeed("ping -c5 10.144.144.3")
|
||||
|
||||
# Relay is accessible by the other hosts through EasyTier
|
||||
peer1.succeed("ping -c5 10.144.144.1")
|
||||
peer2.succeed("ping -c5 10.144.144.1")
|
||||
|
||||
# Relay can access the other hosts through EasyTier
|
||||
relay.succeed("ping -c5 10.144.144.2")
|
||||
relay.succeed("ping -c5 10.144.144.3")
|
||||
|
||||
# The other hosts in separate vlans can access each other through EasyTier
|
||||
peer1.succeed("ping -c5 10.144.144.3")
|
||||
peer2.succeed("ping -c5 10.144.144.2")
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -1,78 +1,81 @@
|
||||
import ../make-test-python.nix (
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
inherit (import ./../ssh-keys.nix pkgs)
|
||||
snakeOilPrivateKey
|
||||
snakeOilPublicKey
|
||||
;
|
||||
{
|
||||
lib,
|
||||
pkgs,
|
||||
hostPkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (import ./../ssh-keys.nix hostPkgs)
|
||||
snakeOilPrivateKey
|
||||
snakeOilPublicKey
|
||||
;
|
||||
|
||||
# don't check host keys or known hosts, use the snakeoil ssh key
|
||||
ssh-config = builtins.toFile "ssh.conf" ''
|
||||
UserKnownHostsFile=/dev/null
|
||||
StrictHostKeyChecking=no
|
||||
IdentityFile=~/.ssh/id_snakeoil
|
||||
'';
|
||||
in
|
||||
{
|
||||
name = "google-oslogin";
|
||||
meta = with pkgs.lib.maintainers; {
|
||||
maintainers = [ ];
|
||||
};
|
||||
# don't check host keys or known hosts, use the snakeoil ssh key
|
||||
ssh-config = builtins.toFile "ssh.conf" ''
|
||||
UserKnownHostsFile=/dev/null
|
||||
StrictHostKeyChecking=no
|
||||
IdentityFile=~/.ssh/id_snakeoil
|
||||
'';
|
||||
in
|
||||
{
|
||||
name = "google-oslogin";
|
||||
meta = with lib.maintainers; {
|
||||
maintainers = [ ];
|
||||
};
|
||||
|
||||
nodes = {
|
||||
# the server provides both the the mocked google metadata server and the ssh server
|
||||
server = (import ./server.nix pkgs);
|
||||
nodes = {
|
||||
# the server provides both the the mocked google metadata server and the ssh server
|
||||
server = ./server.nix;
|
||||
|
||||
client = { ... }: { };
|
||||
};
|
||||
testScript = ''
|
||||
MOCKUSER = "mockuser_nixos_org"
|
||||
MOCKADMIN = "mockadmin_nixos_org"
|
||||
start_all()
|
||||
client = { ... }: { };
|
||||
};
|
||||
testScript = ''
|
||||
MOCKUSER = "mockuser_nixos_org"
|
||||
MOCKADMIN = "mockadmin_nixos_org"
|
||||
start_all()
|
||||
|
||||
server.wait_for_unit("mock-google-metadata.service")
|
||||
server.wait_for_open_port(80)
|
||||
server.wait_for_unit("mock-google-metadata.service")
|
||||
server.wait_for_open_port(80)
|
||||
|
||||
# mockserver should return a non-expired ssh key for both mockuser and mockadmin
|
||||
server.succeed(
|
||||
f'${pkgs.google-guest-oslogin}/bin/google_authorized_keys {MOCKUSER} | grep -q "${snakeOilPublicKey}"'
|
||||
)
|
||||
server.succeed(
|
||||
f'${pkgs.google-guest-oslogin}/bin/google_authorized_keys {MOCKADMIN} | grep -q "${snakeOilPublicKey}"'
|
||||
)
|
||||
# mockserver should return a non-expired ssh key for both mockuser and mockadmin
|
||||
server.succeed(
|
||||
f'${pkgs.google-guest-oslogin}/bin/google_authorized_keys {MOCKUSER} | grep -q "${snakeOilPublicKey}"'
|
||||
)
|
||||
server.succeed(
|
||||
f'${pkgs.google-guest-oslogin}/bin/google_authorized_keys {MOCKADMIN} | grep -q "${snakeOilPublicKey}"'
|
||||
)
|
||||
|
||||
# install snakeoil ssh key on the client, and provision .ssh/config file
|
||||
client.succeed("mkdir -p ~/.ssh")
|
||||
client.succeed(
|
||||
"cat ${snakeOilPrivateKey} > ~/.ssh/id_snakeoil"
|
||||
)
|
||||
client.succeed("chmod 600 ~/.ssh/id_snakeoil")
|
||||
client.succeed("cp ${ssh-config} ~/.ssh/config")
|
||||
# install snakeoil ssh key on the client, and provision .ssh/config file
|
||||
client.succeed("mkdir -p ~/.ssh")
|
||||
client.succeed(
|
||||
"cat ${snakeOilPrivateKey} > ~/.ssh/id_snakeoil"
|
||||
)
|
||||
client.succeed("chmod 600 ~/.ssh/id_snakeoil")
|
||||
client.succeed("cp ${ssh-config} ~/.ssh/config")
|
||||
|
||||
client.wait_for_unit("network.target")
|
||||
server.wait_for_unit("sshd.service")
|
||||
client.wait_for_unit("network.target")
|
||||
server.wait_for_unit("sshd.service")
|
||||
|
||||
# we should not be able to connect as non-existing user
|
||||
client.fail("ssh ghost@server 'true'")
|
||||
# we should not be able to connect as non-existing user
|
||||
client.fail("ssh ghost@server 'true'")
|
||||
|
||||
# we should be able to connect as mockuser
|
||||
client.succeed(f"ssh {MOCKUSER}@server 'true'")
|
||||
# but we shouldn't be able to sudo
|
||||
client.fail(
|
||||
f"ssh {MOCKUSER}@server '/run/wrappers/bin/sudo /run/current-system/sw/bin/id' | grep -q 'root'"
|
||||
)
|
||||
# we should be able to connect as mockuser
|
||||
client.succeed(f"ssh {MOCKUSER}@server 'true'")
|
||||
# but we shouldn't be able to sudo
|
||||
client.fail(
|
||||
f"ssh {MOCKUSER}@server '/run/wrappers/bin/sudo /run/current-system/sw/bin/id' | grep -q 'root'"
|
||||
)
|
||||
|
||||
# we should also be able to log in as mockadmin
|
||||
client.succeed(f"ssh {MOCKADMIN}@server 'true'")
|
||||
# pam_oslogin_admin.so should now have generated a sudoers file
|
||||
server.succeed(
|
||||
f"find /run/google-sudoers.d | grep -q '/run/google-sudoers.d/{MOCKADMIN}'"
|
||||
)
|
||||
# we should also be able to log in as mockadmin
|
||||
client.succeed(f"ssh {MOCKADMIN}@server 'true'")
|
||||
# pam_oslogin_admin.so should now have generated a sudoers file
|
||||
server.succeed(
|
||||
f"find /run/google-sudoers.d | grep -q '/run/google-sudoers.d/{MOCKADMIN}'"
|
||||
)
|
||||
|
||||
# and we should be able to sudo
|
||||
client.succeed(
|
||||
f"ssh {MOCKADMIN}@server '/run/wrappers/bin/sudo /run/current-system/sw/bin/id' | grep -q 'root'"
|
||||
)
|
||||
'';
|
||||
}
|
||||
)
|
||||
# and we should be able to sudo
|
||||
client.succeed(
|
||||
f"ssh {MOCKADMIN}@server '/run/wrappers/bin/sudo /run/current-system/sw/bin/id' | grep -q 'root'"
|
||||
)
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ./make-test-python.nix {
|
||||
{
|
||||
name = "mediatomb";
|
||||
|
||||
nodes = {
|
||||
|
||||
+73
-76
@@ -1,98 +1,95 @@
|
||||
import ./make-test-python.nix (
|
||||
{ pkgs, ... }:
|
||||
{ lib, hostPkgs, ... }:
|
||||
|
||||
let
|
||||
# Make sure we don't have to go through the startup tutorial
|
||||
customMuseScoreConfig = pkgs.writeText "MuseScore4.ini" ''
|
||||
[application]
|
||||
hasCompletedFirstLaunchSetup=true
|
||||
let
|
||||
# Make sure we don't have to go through the startup tutorial
|
||||
customMuseScoreConfig = hostPkgs.writeText "MuseScore4.ini" ''
|
||||
[application]
|
||||
hasCompletedFirstLaunchSetup=true
|
||||
|
||||
[project]
|
||||
preferredScoreCreationMode=1
|
||||
'';
|
||||
in
|
||||
{
|
||||
name = "musescore";
|
||||
meta = with pkgs.lib.maintainers; {
|
||||
maintainers = [ turion ];
|
||||
[project]
|
||||
preferredScoreCreationMode=1
|
||||
'';
|
||||
in
|
||||
{
|
||||
name = "musescore";
|
||||
meta = with lib.maintainers; {
|
||||
maintainers = [ turion ];
|
||||
};
|
||||
|
||||
nodes.machine =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
imports = [
|
||||
./common/x11.nix
|
||||
];
|
||||
|
||||
services.xserver.enable = true;
|
||||
environment.systemPackages = with pkgs; [
|
||||
musescore
|
||||
pdfgrep
|
||||
];
|
||||
};
|
||||
|
||||
nodes.machine =
|
||||
{ ... }:
|
||||
enableOCR = true;
|
||||
|
||||
{
|
||||
imports = [
|
||||
./common/x11.nix
|
||||
];
|
||||
testScript =
|
||||
{ ... }:
|
||||
''
|
||||
start_all()
|
||||
machine.wait_for_x()
|
||||
|
||||
services.xserver.enable = true;
|
||||
environment.systemPackages = with pkgs; [
|
||||
musescore
|
||||
pdfgrep
|
||||
];
|
||||
};
|
||||
# Inject custom settings
|
||||
machine.succeed("mkdir -p /root/.config/MuseScore/")
|
||||
machine.succeed(
|
||||
"cp ${customMuseScoreConfig} /root/.config/MuseScore/MuseScore4.ini"
|
||||
)
|
||||
|
||||
enableOCR = true;
|
||||
# Start MuseScore window
|
||||
machine.execute("env XDG_RUNTIME_DIR=$PWD DISPLAY=:0.0 mscore >&2 &")
|
||||
|
||||
testScript =
|
||||
{ ... }:
|
||||
''
|
||||
start_all()
|
||||
machine.wait_for_x()
|
||||
# Wait until MuseScore has launched
|
||||
machine.wait_for_window("MuseScore Studio")
|
||||
|
||||
# Inject custom settings
|
||||
machine.succeed("mkdir -p /root/.config/MuseScore/")
|
||||
machine.succeed(
|
||||
"cp ${customMuseScoreConfig} /root/.config/MuseScore/MuseScore4.ini"
|
||||
)
|
||||
machine.screenshot("MuseScore0")
|
||||
|
||||
# Start MuseScore window
|
||||
machine.execute("env XDG_RUNTIME_DIR=$PWD DISPLAY=:0.0 mscore >&2 &")
|
||||
# Create a new score
|
||||
machine.send_key("ctrl-n")
|
||||
|
||||
# Wait until MuseScore has launched
|
||||
machine.wait_for_window("MuseScore Studio")
|
||||
# Wait until the creation wizard appears
|
||||
machine.wait_for_window("New score")
|
||||
|
||||
machine.screenshot("MuseScore0")
|
||||
machine.screenshot("MuseScore1")
|
||||
|
||||
# Create a new score
|
||||
machine.send_key("ctrl-n")
|
||||
machine.send_key("tab")
|
||||
machine.send_key("tab")
|
||||
machine.send_key("ret")
|
||||
|
||||
# Wait until the creation wizard appears
|
||||
machine.wait_for_window("New score")
|
||||
machine.sleep(2)
|
||||
|
||||
machine.screenshot("MuseScore1")
|
||||
machine.send_key("tab")
|
||||
# Type the beginning of https://de.wikipedia.org/wiki/Alle_meine_Entchen
|
||||
machine.send_chars("cdef6gg5aaaa7g")
|
||||
machine.sleep(1)
|
||||
|
||||
machine.send_key("tab")
|
||||
machine.send_key("tab")
|
||||
machine.send_key("ret")
|
||||
machine.screenshot("MuseScore2")
|
||||
|
||||
machine.sleep(2)
|
||||
# Go to the export dialogue and create a PDF
|
||||
machine.send_key("ctrl-p")
|
||||
|
||||
machine.send_key("tab")
|
||||
# Type the beginning of https://de.wikipedia.org/wiki/Alle_meine_Entchen
|
||||
machine.send_chars("cdef6gg5aaaa7g")
|
||||
machine.sleep(1)
|
||||
# Wait until the Print dialogue appears.
|
||||
machine.wait_for_window("Print")
|
||||
|
||||
machine.screenshot("MuseScore2")
|
||||
machine.screenshot("MuseScore4")
|
||||
machine.send_key("alt-p")
|
||||
machine.sleep(1)
|
||||
|
||||
# Go to the export dialogue and create a PDF
|
||||
machine.send_key("ctrl-p")
|
||||
machine.screenshot("MuseScore5")
|
||||
|
||||
# Wait until the Print dialogue appears.
|
||||
machine.wait_for_window("Print")
|
||||
# Wait until PDF is exported
|
||||
machine.wait_for_file('"/root/Untitled score.pdf"')
|
||||
|
||||
machine.screenshot("MuseScore4")
|
||||
machine.send_key("alt-p")
|
||||
machine.sleep(1)
|
||||
|
||||
machine.screenshot("MuseScore5")
|
||||
|
||||
# Wait until PDF is exported
|
||||
machine.wait_for_file('"/root/Untitled score.pdf"')
|
||||
|
||||
## Check that it contains the title of the score
|
||||
machine.succeed('pdfgrep "Untitled score" "/root/Untitled score.pdf"')
|
||||
machine.copy_from_vm("/root/Untitled score.pdf")
|
||||
'';
|
||||
}
|
||||
)
|
||||
## Check that it contains the title of the score
|
||||
machine.succeed('pdfgrep "Untitled score" "/root/Untitled score.pdf"')
|
||||
machine.copy_from_vm("/root/Untitled score.pdf")
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -1,73 +1,66 @@
|
||||
import ../make-test-python.nix (
|
||||
{ lib, ... }:
|
||||
let
|
||||
peer1-ip = "538:f40f:1c51:9bd9:9569:d3f6:d0a1:b2df";
|
||||
peer2-ip = "5b6:6776:fee0:c1f3:db00:b6a8:d013:d38f";
|
||||
in
|
||||
{
|
||||
name = "mycelium";
|
||||
meta.maintainers = with lib.maintainers; [ lassulus ];
|
||||
{ lib, ... }:
|
||||
let
|
||||
peer1-ip = "538:f40f:1c51:9bd9:9569:d3f6:d0a1:b2df";
|
||||
peer2-ip = "5b6:6776:fee0:c1f3:db00:b6a8:d013:d38f";
|
||||
in
|
||||
{
|
||||
name = "mycelium";
|
||||
meta.maintainers = with lib.maintainers; [ lassulus ];
|
||||
|
||||
nodes = {
|
||||
|
||||
peer1 =
|
||||
{ config, pkgs, ... }:
|
||||
nodes = {
|
||||
peer1 = {
|
||||
virtualisation.vlans = [ 1 ];
|
||||
networking.interfaces.eth1.ipv4.addresses = [
|
||||
{
|
||||
virtualisation.vlans = [ 1 ];
|
||||
networking.interfaces.eth1.ipv4.addresses = [
|
||||
{
|
||||
address = "192.168.1.11";
|
||||
prefixLength = 24;
|
||||
}
|
||||
];
|
||||
address = "192.168.1.11";
|
||||
prefixLength = 24;
|
||||
}
|
||||
];
|
||||
|
||||
services.mycelium = {
|
||||
enable = true;
|
||||
addHostedPublicNodes = false;
|
||||
openFirewall = true;
|
||||
keyFile = ./peer1.key;
|
||||
peers = [
|
||||
"quic://192.168.1.12:9651"
|
||||
"tcp://192.168.1.12:9651"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
peer2 =
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
virtualisation.vlans = [ 1 ];
|
||||
networking.interfaces.eth1.ipv4.addresses = [
|
||||
{
|
||||
address = "192.168.1.12";
|
||||
prefixLength = 24;
|
||||
}
|
||||
];
|
||||
|
||||
services.mycelium = {
|
||||
enable = true;
|
||||
addHostedPublicNodes = false;
|
||||
openFirewall = true;
|
||||
keyFile = ./peer2.key;
|
||||
};
|
||||
};
|
||||
services.mycelium = {
|
||||
enable = true;
|
||||
addHostedPublicNodes = false;
|
||||
openFirewall = true;
|
||||
keyFile = ./peer1.key;
|
||||
peers = [
|
||||
"quic://192.168.1.12:9651"
|
||||
"tcp://192.168.1.12:9651"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
peer2 = {
|
||||
virtualisation.vlans = [ 1 ];
|
||||
networking.interfaces.eth1.ipv4.addresses = [
|
||||
{
|
||||
address = "192.168.1.12";
|
||||
prefixLength = 24;
|
||||
}
|
||||
];
|
||||
|
||||
peer1.wait_for_unit("network-online.target")
|
||||
peer2.wait_for_unit("network-online.target")
|
||||
peer1.wait_for_unit("mycelium.service")
|
||||
peer2.wait_for_unit("mycelium.service")
|
||||
services.mycelium = {
|
||||
enable = true;
|
||||
addHostedPublicNodes = false;
|
||||
openFirewall = true;
|
||||
keyFile = ./peer2.key;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Give mycelium some time to discover the other peer
|
||||
peer1.wait_until_succeeds("ping -c1 ${peer2-ip}", timeout=10)
|
||||
peer2.succeed("ping -c1 ${peer1-ip}")
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
peer1.succeed("mycelium peers list | grep 192.168.1.12")
|
||||
peer2.succeed("mycelium peers list | grep 192.168.1.11")
|
||||
peer1.wait_for_unit("network-online.target")
|
||||
peer2.wait_for_unit("network-online.target")
|
||||
peer1.wait_for_unit("mycelium.service")
|
||||
peer2.wait_for_unit("mycelium.service")
|
||||
|
||||
'';
|
||||
}
|
||||
)
|
||||
# Give mycelium some time to discover the other peer
|
||||
peer1.wait_until_succeeds("ping -c1 ${peer2-ip}", timeout=10)
|
||||
peer2.succeed("ping -c1 ${peer1-ip}")
|
||||
|
||||
peer1.succeed("mycelium peers list | grep 192.168.1.12")
|
||||
peer2.succeed("mycelium peers list | grep 192.168.1.11")
|
||||
|
||||
'';
|
||||
}
|
||||
|
||||
+8
-13
@@ -1,19 +1,14 @@
|
||||
import ./make-test-python.nix {
|
||||
{
|
||||
name = "neo4j";
|
||||
|
||||
nodes = {
|
||||
server =
|
||||
{ ... }:
|
||||
nodes.server = {
|
||||
virtualisation.memorySize = 4096;
|
||||
virtualisation.diskSize = 1024;
|
||||
|
||||
{
|
||||
virtualisation.memorySize = 4096;
|
||||
virtualisation.diskSize = 1024;
|
||||
|
||||
services.neo4j.enable = true;
|
||||
# require tls certs to be available
|
||||
services.neo4j.https.enable = false;
|
||||
services.neo4j.bolt.enable = false;
|
||||
};
|
||||
services.neo4j.enable = true;
|
||||
# require tls certs to be available
|
||||
services.neo4j.https.enable = false;
|
||||
services.neo4j.bolt.enable = false;
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
|
||||
@@ -2,7 +2,7 @@ let
|
||||
certs = import ./common/acme/server/snakeoil-certs.nix;
|
||||
domain = certs.domain;
|
||||
in
|
||||
import ./make-test-python.nix {
|
||||
{
|
||||
name = "schleuder";
|
||||
nodes.machine =
|
||||
{ pkgs, ... }:
|
||||
|
||||
+51
-66
@@ -1,77 +1,62 @@
|
||||
{ runTest }:
|
||||
{
|
||||
system ? builtins.currentSystem,
|
||||
config ? { },
|
||||
pkgs ? import ../.. { inherit system config; },
|
||||
}:
|
||||
default = runTest {
|
||||
name = "sddm";
|
||||
|
||||
with import ../lib/testing-python.nix { inherit system pkgs; };
|
||||
|
||||
let
|
||||
inherit (pkgs) lib;
|
||||
|
||||
tests = {
|
||||
default = {
|
||||
name = "sddm";
|
||||
|
||||
nodes.machine =
|
||||
{ ... }:
|
||||
{
|
||||
imports = [ ./common/user-account.nix ];
|
||||
services.xserver.enable = true;
|
||||
services.displayManager.sddm.enable = true;
|
||||
services.displayManager.defaultSession = "none+icewm";
|
||||
services.xserver.windowManager.icewm.enable = true;
|
||||
};
|
||||
|
||||
enableOCR = true;
|
||||
|
||||
testScript =
|
||||
{ nodes, ... }:
|
||||
let
|
||||
user = nodes.machine.users.users.alice;
|
||||
in
|
||||
''
|
||||
start_all()
|
||||
machine.wait_for_text("(?i)select your user")
|
||||
machine.screenshot("sddm")
|
||||
machine.send_chars("${user.password}\n")
|
||||
machine.wait_for_file("/tmp/xauth_*")
|
||||
machine.succeed("xauth merge /tmp/xauth_*")
|
||||
machine.wait_for_window("^IceWM ")
|
||||
'';
|
||||
nodes.machine = {
|
||||
imports = [ ./common/user-account.nix ];
|
||||
services.xserver.enable = true;
|
||||
services.displayManager.sddm.enable = true;
|
||||
services.displayManager.defaultSession = "none+icewm";
|
||||
services.xserver.windowManager.icewm.enable = true;
|
||||
};
|
||||
|
||||
autoLogin = {
|
||||
enableOCR = true;
|
||||
|
||||
testScript =
|
||||
{ nodes, ... }:
|
||||
let
|
||||
user = nodes.machine.users.users.alice;
|
||||
in
|
||||
''
|
||||
start_all()
|
||||
machine.wait_for_text("(?i)select your user")
|
||||
machine.screenshot("sddm")
|
||||
machine.send_chars("${user.password}\n")
|
||||
machine.wait_for_file("/tmp/xauth_*")
|
||||
machine.succeed("xauth merge /tmp/xauth_*")
|
||||
machine.wait_for_window("^IceWM ")
|
||||
'';
|
||||
};
|
||||
|
||||
autoLogin = runTest (
|
||||
{ lib, ... }:
|
||||
{
|
||||
name = "sddm-autologin";
|
||||
meta = with pkgs.lib.maintainers; {
|
||||
meta = with lib.maintainers; {
|
||||
maintainers = [ ttuegel ];
|
||||
};
|
||||
|
||||
nodes.machine =
|
||||
{ ... }:
|
||||
{
|
||||
imports = [ ./common/user-account.nix ];
|
||||
services.xserver.enable = true;
|
||||
services.displayManager = {
|
||||
sddm.enable = true;
|
||||
autoLogin = {
|
||||
enable = true;
|
||||
user = "alice";
|
||||
};
|
||||
nodes.machine = {
|
||||
imports = [ ./common/user-account.nix ];
|
||||
services.xserver.enable = true;
|
||||
services.displayManager = {
|
||||
sddm.enable = true;
|
||||
autoLogin = {
|
||||
enable = true;
|
||||
user = "alice";
|
||||
};
|
||||
services.displayManager.defaultSession = "none+icewm";
|
||||
services.xserver.windowManager.icewm.enable = true;
|
||||
};
|
||||
services.displayManager.defaultSession = "none+icewm";
|
||||
services.xserver.windowManager.icewm.enable = true;
|
||||
};
|
||||
|
||||
testScript =
|
||||
{ nodes, ... }:
|
||||
''
|
||||
start_all()
|
||||
machine.wait_for_file("/tmp/xauth_*")
|
||||
machine.succeed("xauth merge /tmp/xauth_*")
|
||||
machine.wait_for_window("^IceWM ")
|
||||
'';
|
||||
};
|
||||
};
|
||||
in
|
||||
lib.mapAttrs (lib.const makeTest) tests
|
||||
testScript = ''
|
||||
start_all()
|
||||
machine.wait_for_file("/tmp/xauth_*")
|
||||
machine.succeed("xauth merge /tmp/xauth_*")
|
||||
machine.wait_for_window("^IceWM ")
|
||||
'';
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,20 +24,23 @@
|
||||
serviceConfig.Type = "oneshot";
|
||||
};
|
||||
|
||||
networking.primaryIPAddress = "192.168.1.${toString config.virtualisation.test.nodeNumber}";
|
||||
networking.primaryIPAddress = lib.mkForce "192.168.1.${toString config.virtualisation.test.nodeNumber}";
|
||||
|
||||
virtualisation.interfaces.eth1 = {
|
||||
vlan = 1;
|
||||
assignIP = false;
|
||||
};
|
||||
virtualisation.interfaces.eth2 = {
|
||||
vlan = 2;
|
||||
assignIP = false;
|
||||
};
|
||||
|
||||
virtualisation.vlans = [
|
||||
1
|
||||
2
|
||||
];
|
||||
networking.bridges.br0.interfaces = [
|
||||
"eth1"
|
||||
"eth2"
|
||||
];
|
||||
|
||||
networking.interfaces = {
|
||||
eth1.ipv4.addresses = lib.mkForce [ ];
|
||||
eth2.ipv4.addresses = lib.mkForce [ ];
|
||||
br0.ipv4.addresses = [
|
||||
{
|
||||
address = config.networking.primaryIPAddress;
|
||||
|
||||
@@ -1,55 +1,52 @@
|
||||
import ./make-test-python.nix (
|
||||
{ ... }:
|
||||
{
|
||||
name = "trilium-server";
|
||||
nodes = {
|
||||
default = {
|
||||
services.trilium-server.enable = true;
|
||||
};
|
||||
configured = {
|
||||
services.trilium-server = {
|
||||
enable = true;
|
||||
dataDir = "/data/trilium";
|
||||
};
|
||||
};
|
||||
|
||||
nginx = {
|
||||
services.trilium-server = {
|
||||
enable = true;
|
||||
nginx.enable = true;
|
||||
nginx.hostName = "trilium.example.com";
|
||||
};
|
||||
{
|
||||
name = "trilium-server";
|
||||
nodes = {
|
||||
default = {
|
||||
services.trilium-server.enable = true;
|
||||
};
|
||||
configured = {
|
||||
services.trilium-server = {
|
||||
enable = true;
|
||||
dataDir = "/data/trilium";
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
nginx = {
|
||||
services.trilium-server = {
|
||||
enable = true;
|
||||
nginx.enable = true;
|
||||
nginx.hostName = "trilium.example.com";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
with subtest("by default works without configuration"):
|
||||
default.wait_for_unit("trilium-server.service")
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
with subtest("by default available on port 8080"):
|
||||
default.wait_for_unit("trilium-server.service")
|
||||
default.wait_for_open_port(8080)
|
||||
# we output to /dev/null here to avoid a python UTF-8 decode error
|
||||
# but the check will still fail if the service doesn't respond
|
||||
default.succeed("curl --fail -o /dev/null 127.0.0.1:8080")
|
||||
with subtest("by default works without configuration"):
|
||||
default.wait_for_unit("trilium-server.service")
|
||||
|
||||
with subtest("by default creates empty document"):
|
||||
default.wait_for_unit("trilium-server.service")
|
||||
default.succeed("test -f /var/lib/trilium/document.db")
|
||||
with subtest("by default available on port 8080"):
|
||||
default.wait_for_unit("trilium-server.service")
|
||||
default.wait_for_open_port(8080)
|
||||
# we output to /dev/null here to avoid a python UTF-8 decode error
|
||||
# but the check will still fail if the service doesn't respond
|
||||
default.succeed("curl --fail -o /dev/null 127.0.0.1:8080")
|
||||
|
||||
with subtest("configured with custom data store"):
|
||||
configured.wait_for_unit("trilium-server.service")
|
||||
configured.succeed("test -f /data/trilium/document.db")
|
||||
with subtest("by default creates empty document"):
|
||||
default.wait_for_unit("trilium-server.service")
|
||||
default.succeed("test -f /var/lib/trilium/document.db")
|
||||
|
||||
with subtest("nginx with custom host name"):
|
||||
nginx.wait_for_unit("trilium-server.service")
|
||||
nginx.wait_for_unit("nginx.service")
|
||||
with subtest("configured with custom data store"):
|
||||
configured.wait_for_unit("trilium-server.service")
|
||||
configured.succeed("test -f /data/trilium/document.db")
|
||||
|
||||
nginx.succeed(
|
||||
"curl --resolve 'trilium.example.com:80:127.0.0.1' http://trilium.example.com/"
|
||||
)
|
||||
'';
|
||||
}
|
||||
)
|
||||
with subtest("nginx with custom host name"):
|
||||
nginx.wait_for_unit("trilium-server.service")
|
||||
nginx.wait_for_unit("nginx.service")
|
||||
|
||||
nginx.succeed(
|
||||
"curl --resolve 'trilium.example.com:80:127.0.0.1' http://trilium.example.com/"
|
||||
)
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
{ runTest }:
|
||||
{
|
||||
system ? builtins.currentSystem,
|
||||
config ? { },
|
||||
pkgs ? import ../../.. { inherit system config; },
|
||||
}:
|
||||
|
||||
{
|
||||
remote-write = import ./remote-write.nix { inherit system pkgs; };
|
||||
vmalert = import ./vmalert.nix { inherit system pkgs; };
|
||||
external-promscrape-config = import ./external-promscrape-config.nix { inherit system pkgs; };
|
||||
remote-write = runTest ./remote-write.nix;
|
||||
vmalert = runTest ./vmalert.nix;
|
||||
external-promscrape-config = runTest ./external-promscrape-config.nix;
|
||||
}
|
||||
|
||||
@@ -1,82 +1,70 @@
|
||||
import ../make-test-python.nix (
|
||||
{
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
nodeExporterPort = 9100;
|
||||
promscrapeConfig = {
|
||||
global = {
|
||||
scrape_interval = "2s";
|
||||
};
|
||||
scrape_configs = [
|
||||
{
|
||||
job_name = "node";
|
||||
static_configs = [
|
||||
{
|
||||
targets = [
|
||||
"node:${toString nodeExporterPort}"
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
{ lib, pkgs, ... }:
|
||||
let
|
||||
nodeExporterPort = 9100;
|
||||
promscrapeConfig = {
|
||||
global = {
|
||||
scrape_interval = "2s";
|
||||
};
|
||||
settingsFormat = pkgs.formats.yaml { };
|
||||
promscrapeConfigYaml = settingsFormat.generate "prometheusConfig.yaml" promscrapeConfig;
|
||||
in
|
||||
{
|
||||
name = "victoriametrics-external-promscrape-config";
|
||||
meta = with pkgs.lib.maintainers; {
|
||||
maintainers = [
|
||||
ryan4yin
|
||||
];
|
||||
};
|
||||
|
||||
nodes = {
|
||||
victoriametrics =
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
environment.systemPackages = [ pkgs.jq ];
|
||||
networking.firewall.allowedTCPPorts = [ 8428 ];
|
||||
services.victoriametrics = {
|
||||
enable = true;
|
||||
extraOptions = [
|
||||
"-promscrape.config=${toString promscrapeConfigYaml}"
|
||||
scrape_configs = [
|
||||
{
|
||||
job_name = "node";
|
||||
static_configs = [
|
||||
{
|
||||
targets = [
|
||||
"node:${toString nodeExporterPort}"
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
settingsFormat = pkgs.formats.yaml { };
|
||||
promscrapeConfigYaml = settingsFormat.generate "prometheusConfig.yaml" promscrapeConfig;
|
||||
in
|
||||
{
|
||||
name = "victoriametrics-external-promscrape-config";
|
||||
meta = with lib.maintainers; {
|
||||
maintainers = [
|
||||
ryan4yin
|
||||
];
|
||||
};
|
||||
|
||||
node =
|
||||
{ ... }:
|
||||
{
|
||||
services.prometheus.exporters.node = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
nodes = {
|
||||
victoriametrics =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
environment.systemPackages = [ pkgs.jq ];
|
||||
networking.firewall.allowedTCPPorts = [ 8428 ];
|
||||
services.victoriametrics = {
|
||||
enable = true;
|
||||
extraOptions = [
|
||||
"-promscrape.config=${toString promscrapeConfigYaml}"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
node = {
|
||||
services.prometheus.exporters.node = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
node.wait_for_unit("prometheus-node-exporter")
|
||||
node.wait_for_open_port(${toString nodeExporterPort})
|
||||
testScript = ''
|
||||
node.wait_for_unit("prometheus-node-exporter")
|
||||
node.wait_for_open_port(${toString nodeExporterPort})
|
||||
|
||||
victoriametrics.wait_for_unit("victoriametrics")
|
||||
victoriametrics.wait_for_open_port(8428)
|
||||
victoriametrics.wait_for_unit("victoriametrics")
|
||||
victoriametrics.wait_for_open_port(8428)
|
||||
|
||||
|
||||
promscrape_config = victoriametrics.succeed("journalctl -u victoriametrics -o cat | grep 'promscrape.config'")
|
||||
assert '${toString promscrapeConfigYaml}' in promscrape_config
|
||||
promscrape_config = victoriametrics.succeed("journalctl -u victoriametrics -o cat | grep 'promscrape.config'")
|
||||
assert '${toString promscrapeConfigYaml}' in promscrape_config
|
||||
|
||||
victoriametrics.wait_until_succeeds(
|
||||
"curl -sf 'http://localhost:8428/api/v1/query?query=node_exporter_build_info\{instance=\"node:9100\"\}' | "
|
||||
+ "jq '.data.result[0].value[1]' | grep '\"1\"'"
|
||||
)
|
||||
'';
|
||||
}
|
||||
)
|
||||
victoriametrics.wait_until_succeeds(
|
||||
"curl -sf 'http://localhost:8428/api/v1/query?query=node_exporter_build_info\{instance=\"node:9100\"\}' | "
|
||||
+ "jq '.data.result[0].value[1]' | grep '\"1\"'"
|
||||
)
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -1,103 +1,87 @@
|
||||
# Primarily reference the implementation of <nixos/tests/prometheus/remote-write.nix>
|
||||
import ../make-test-python.nix (
|
||||
{
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
username = "vmtest";
|
||||
password = "fsddfy8233rb"; # random string
|
||||
passwordFile = pkgs.writeText "password-file" password;
|
||||
in
|
||||
{
|
||||
name = "victoriametrics-remote-write";
|
||||
meta = with pkgs.lib.maintainers; {
|
||||
maintainers = [
|
||||
yorickvp
|
||||
ryan4yin
|
||||
];
|
||||
};
|
||||
{ lib, pkgs, ... }:
|
||||
let
|
||||
username = "vmtest";
|
||||
password = "fsddfy8233rb"; # random string
|
||||
passwordFile = pkgs.writeText "password-file" password;
|
||||
in
|
||||
{
|
||||
name = "victoriametrics-remote-write";
|
||||
meta = with lib.maintainers; {
|
||||
maintainers = [
|
||||
yorickvp
|
||||
ryan4yin
|
||||
];
|
||||
};
|
||||
|
||||
nodes = {
|
||||
victoriametrics =
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
environment.systemPackages = [ pkgs.jq ];
|
||||
networking.firewall.allowedTCPPorts = [ 8428 ];
|
||||
services.victoriametrics = {
|
||||
enable = true;
|
||||
extraOptions = [
|
||||
"-httpAuth.username=${username}"
|
||||
"-httpAuth.password=file://${toString passwordFile}"
|
||||
nodes = {
|
||||
victoriametrics =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
environment.systemPackages = [ pkgs.jq ];
|
||||
networking.firewall.allowedTCPPorts = [ 8428 ];
|
||||
services.victoriametrics = {
|
||||
enable = true;
|
||||
extraOptions = [
|
||||
"-httpAuth.username=${username}"
|
||||
"-httpAuth.password=file://${toString passwordFile}"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
vmagent =
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
environment.systemPackages = [ pkgs.jq ];
|
||||
services.vmagent = {
|
||||
enable = true;
|
||||
remoteWrite = {
|
||||
url = "http://victoriametrics:8428/api/v1/write";
|
||||
basicAuthUsername = username;
|
||||
basicAuthPasswordFile = toString passwordFile;
|
||||
};
|
||||
|
||||
prometheusConfig = {
|
||||
global = {
|
||||
scrape_interval = "2s";
|
||||
};
|
||||
scrape_configs = [
|
||||
{
|
||||
job_name = "node";
|
||||
static_configs = [
|
||||
{
|
||||
targets = [
|
||||
"node:${toString config.services.prometheus.exporters.node.port}"
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
vmagent =
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
environment.systemPackages = [ pkgs.jq ];
|
||||
services.vmagent = {
|
||||
enable = true;
|
||||
remoteWrite = {
|
||||
url = "http://victoriametrics:8428/api/v1/write";
|
||||
basicAuthUsername = username;
|
||||
basicAuthPasswordFile = toString passwordFile;
|
||||
};
|
||||
|
||||
prometheusConfig = {
|
||||
global = {
|
||||
scrape_interval = "2s";
|
||||
};
|
||||
scrape_configs = [
|
||||
{
|
||||
job_name = "node";
|
||||
static_configs = [
|
||||
{
|
||||
targets = [
|
||||
"node:${toString config.services.prometheus.exporters.node.port}"
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
node =
|
||||
{ ... }:
|
||||
{
|
||||
services.prometheus.exporters.node = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
};
|
||||
node = {
|
||||
services.prometheus.exporters.node = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
node.wait_for_unit("prometheus-node-exporter")
|
||||
node.wait_for_open_port(9100)
|
||||
testScript = ''
|
||||
node.wait_for_unit("prometheus-node-exporter")
|
||||
node.wait_for_open_port(9100)
|
||||
|
||||
victoriametrics.wait_for_unit("victoriametrics")
|
||||
victoriametrics.wait_for_open_port(8428)
|
||||
victoriametrics.wait_for_unit("victoriametrics")
|
||||
victoriametrics.wait_for_open_port(8428)
|
||||
|
||||
vmagent.wait_for_unit("vmagent")
|
||||
vmagent.wait_for_unit("vmagent")
|
||||
|
||||
# check remote write
|
||||
victoriametrics.wait_until_succeeds(
|
||||
"curl --user '${username}:${password}' -sf 'http://localhost:8428/api/v1/query?query=node_exporter_build_info\{instance=\"node:9100\"\}' | "
|
||||
+ "jq '.data.result[0].value[1]' | grep '\"1\"'"
|
||||
)
|
||||
'';
|
||||
}
|
||||
)
|
||||
# check remote write
|
||||
victoriametrics.wait_until_succeeds(
|
||||
"curl --user '${username}:${password}' -sf 'http://localhost:8428/api/v1/query?query=node_exporter_build_info\{instance=\"node:9100\"\}' | "
|
||||
+ "jq '.data.result[0].value[1]' | grep '\"1\"'"
|
||||
)
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -1,179 +1,157 @@
|
||||
# Primarily reference the implementation of <nixos/tests/prometheus/alertmanager.nix>
|
||||
import ../make-test-python.nix (
|
||||
{
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
name = "victoriametrics-vmalert";
|
||||
meta = with pkgs.lib.maintainers; {
|
||||
maintainers = [
|
||||
yorickvp
|
||||
ryan4yin
|
||||
];
|
||||
{ lib, pkgs, ... }:
|
||||
{
|
||||
name = "victoriametrics-vmalert";
|
||||
meta = with lib.maintainers; {
|
||||
maintainers = [
|
||||
yorickvp
|
||||
ryan4yin
|
||||
];
|
||||
};
|
||||
|
||||
nodes = {
|
||||
victoriametrics =
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
environment.systemPackages = [ pkgs.jq ];
|
||||
networking.firewall.allowedTCPPorts = [ 8428 ];
|
||||
services.victoriametrics = {
|
||||
enable = true;
|
||||
prometheusConfig = {
|
||||
global = {
|
||||
scrape_interval = "2s";
|
||||
};
|
||||
scrape_configs = [
|
||||
{
|
||||
job_name = "alertmanager";
|
||||
static_configs = [
|
||||
{
|
||||
targets = [
|
||||
"alertmanager:${toString config.services.prometheus.alertmanager.port}"
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
job_name = "node";
|
||||
static_configs = [
|
||||
{
|
||||
targets = [
|
||||
"node:${toString config.services.prometheus.exporters.node.port}"
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
services.vmalert.instances."" = {
|
||||
enable = true;
|
||||
settings = {
|
||||
"datasource.url" = "http://localhost:8428"; # victoriametrics' api
|
||||
"notifier.url" = [
|
||||
"http://alertmanager:${toString config.services.prometheus.alertmanager.port}"
|
||||
]; # alertmanager's api
|
||||
rule = [
|
||||
(pkgs.writeText "instance-down.yml" ''
|
||||
groups:
|
||||
- name: test
|
||||
rules:
|
||||
- alert: InstanceDown
|
||||
expr: up == 0
|
||||
for: 5s
|
||||
labels:
|
||||
severity: page
|
||||
annotations:
|
||||
summary: "Instance {{ $labels.instance }} down"
|
||||
'')
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
alertmanager = {
|
||||
services.prometheus.alertmanager = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
|
||||
configuration = {
|
||||
global = {
|
||||
resolve_timeout = "1m";
|
||||
};
|
||||
|
||||
route = {
|
||||
# Root route node
|
||||
receiver = "test";
|
||||
group_by = [ "..." ];
|
||||
continue = false;
|
||||
group_wait = "1s";
|
||||
group_interval = "15s";
|
||||
repeat_interval = "24h";
|
||||
};
|
||||
|
||||
receivers = [
|
||||
{
|
||||
name = "test";
|
||||
webhook_configs = [
|
||||
{
|
||||
url = "http://logger:6725";
|
||||
send_resolved = true;
|
||||
max_alerts = 0;
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nodes = {
|
||||
victoriametrics =
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
environment.systemPackages = [ pkgs.jq ];
|
||||
networking.firewall.allowedTCPPorts = [ 8428 ];
|
||||
services.victoriametrics = {
|
||||
enable = true;
|
||||
prometheusConfig = {
|
||||
global = {
|
||||
scrape_interval = "2s";
|
||||
};
|
||||
scrape_configs = [
|
||||
{
|
||||
job_name = "alertmanager";
|
||||
static_configs = [
|
||||
{
|
||||
targets = [
|
||||
"alertmanager:${toString config.services.prometheus.alertmanager.port}"
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
job_name = "node";
|
||||
static_configs = [
|
||||
{
|
||||
targets = [
|
||||
"node:${toString config.services.prometheus.exporters.node.port}"
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
logger = {
|
||||
networking.firewall.allowedTCPPorts = [ 6725 ];
|
||||
|
||||
services.vmalert.instances."" = {
|
||||
enable = true;
|
||||
settings = {
|
||||
"datasource.url" = "http://localhost:8428"; # victoriametrics' api
|
||||
"notifier.url" = [
|
||||
"http://alertmanager:${toString config.services.prometheus.alertmanager.port}"
|
||||
]; # alertmanager's api
|
||||
rule = [
|
||||
(pkgs.writeText "instance-down.yml" ''
|
||||
groups:
|
||||
- name: test
|
||||
rules:
|
||||
- alert: InstanceDown
|
||||
expr: up == 0
|
||||
for: 5s
|
||||
labels:
|
||||
severity: page
|
||||
annotations:
|
||||
summary: "Instance {{ $labels.instance }} down"
|
||||
'')
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
alertmanager =
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
services.prometheus.alertmanager = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
|
||||
configuration = {
|
||||
global = {
|
||||
resolve_timeout = "1m";
|
||||
};
|
||||
|
||||
route = {
|
||||
# Root route node
|
||||
receiver = "test";
|
||||
group_by = [ "..." ];
|
||||
continue = false;
|
||||
group_wait = "1s";
|
||||
group_interval = "15s";
|
||||
repeat_interval = "24h";
|
||||
};
|
||||
|
||||
receivers = [
|
||||
{
|
||||
name = "test";
|
||||
webhook_configs = [
|
||||
{
|
||||
url = "http://logger:6725";
|
||||
send_resolved = true;
|
||||
max_alerts = 0;
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
logger =
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
networking.firewall.allowedTCPPorts = [ 6725 ];
|
||||
|
||||
services.prometheus.alertmanagerWebhookLogger.enable = true;
|
||||
};
|
||||
services.prometheus.alertmanagerWebhookLogger.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
alertmanager.wait_for_unit("alertmanager")
|
||||
alertmanager.wait_for_open_port(9093)
|
||||
alertmanager.wait_until_succeeds("curl -s http://127.0.0.1:9093/-/ready")
|
||||
testScript = ''
|
||||
alertmanager.wait_for_unit("alertmanager")
|
||||
alertmanager.wait_for_open_port(9093)
|
||||
alertmanager.wait_until_succeeds("curl -s http://127.0.0.1:9093/-/ready")
|
||||
|
||||
logger.wait_for_unit("alertmanager-webhook-logger")
|
||||
logger.wait_for_open_port(6725)
|
||||
logger.wait_for_unit("alertmanager-webhook-logger")
|
||||
logger.wait_for_open_port(6725)
|
||||
|
||||
victoriametrics.wait_for_unit("victoriametrics")
|
||||
victoriametrics.wait_for_unit("vmalert")
|
||||
victoriametrics.wait_for_open_port(8428)
|
||||
victoriametrics.wait_for_unit("victoriametrics")
|
||||
victoriametrics.wait_for_unit("vmalert")
|
||||
victoriametrics.wait_for_open_port(8428)
|
||||
|
||||
victoriametrics.wait_until_succeeds(
|
||||
"curl -sf 'http://127.0.0.1:8428/api/v1/query?query=count(up\{job=\"alertmanager\"\}==1)' | "
|
||||
+ "jq '.data.result[0].value[1]' | grep '\"1\"'"
|
||||
)
|
||||
victoriametrics.wait_until_succeeds(
|
||||
"curl -sf 'http://127.0.0.1:8428/api/v1/query?query=count(up\{job=\"alertmanager\"\}==1)' | "
|
||||
+ "jq '.data.result[0].value[1]' | grep '\"1\"'"
|
||||
)
|
||||
|
||||
victoriametrics.wait_until_succeeds(
|
||||
"curl -sf 'http://127.0.0.1:8428/api/v1/query?query=sum(alertmanager_build_info)%20by%20(version)' | "
|
||||
+ "jq '.data.result[0].metric.version' | grep '\"${pkgs.prometheus-alertmanager.version}\"'"
|
||||
)
|
||||
victoriametrics.wait_until_succeeds(
|
||||
"curl -sf 'http://127.0.0.1:8428/api/v1/query?query=sum(alertmanager_build_info)%20by%20(version)' | "
|
||||
+ "jq '.data.result[0].metric.version' | grep '\"${pkgs.prometheus-alertmanager.version}\"'"
|
||||
)
|
||||
|
||||
victoriametrics.wait_until_succeeds(
|
||||
"curl -sf 'http://127.0.0.1:8428/api/v1/query?query=count(up\{job=\"node\"\}!=1)' | "
|
||||
+ "jq '.data.result[0].value[1]' | grep '\"1\"'"
|
||||
)
|
||||
victoriametrics.wait_until_succeeds(
|
||||
"curl -sf 'http://127.0.0.1:8428/api/v1/query?query=count(up\{job=\"node\"\}!=1)' | "
|
||||
+ "jq '.data.result[0].value[1]' | grep '\"1\"'"
|
||||
)
|
||||
|
||||
victoriametrics.wait_until_succeeds(
|
||||
"curl -sf 'http://127.0.0.1:8428/api/v1/query?query=alertmanager_notifications_total\{integration=\"webhook\"\}' | "
|
||||
+ "jq '.data.result[0].value[1]' | grep -v '\"0\"'"
|
||||
)
|
||||
victoriametrics.wait_until_succeeds(
|
||||
"curl -sf 'http://127.0.0.1:8428/api/v1/query?query=alertmanager_notifications_total\{integration=\"webhook\"\}' | "
|
||||
+ "jq '.data.result[0].value[1]' | grep -v '\"0\"'"
|
||||
)
|
||||
|
||||
logger.wait_until_succeeds(
|
||||
"journalctl -o cat -u alertmanager-webhook-logger.service | grep '\"alertname\":\"InstanceDown\"'"
|
||||
)
|
||||
logger.wait_until_succeeds(
|
||||
"journalctl -o cat -u alertmanager-webhook-logger.service | grep '\"alertname\":\"InstanceDown\"'"
|
||||
)
|
||||
|
||||
logger.log(logger.succeed("systemd-analyze security alertmanager-webhook-logger.service | grep -v '✓'"))
|
||||
logger.log(logger.succeed("systemd-analyze security alertmanager-webhook-logger.service | grep -v '✓'"))
|
||||
|
||||
alertmanager.log(alertmanager.succeed("systemd-analyze security alertmanager.service | grep -v '✓'"))
|
||||
'';
|
||||
}
|
||||
)
|
||||
alertmanager.log(alertmanager.succeed("systemd-analyze security alertmanager.service | grep -v '✓'"))
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
fetchFromGitHub,
|
||||
deadbeef,
|
||||
pkg-config,
|
||||
gtk3,
|
||||
sqlite,
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
pname = "deadbeef-waveform-seekbar-plugin";
|
||||
version = "0-unstable-2024-11-13";
|
||||
|
||||
# using a fork because original throws a compilation error
|
||||
src = fetchFromGitHub {
|
||||
owner = "Jbsco";
|
||||
repo = "ddb_waveform_seekbar";
|
||||
rev = "2e5ea867a77e37698524d22f41fc59ffae16e63d";
|
||||
hash = "sha256-m6lBF+Yq1gah6kjb9VvIsjVg1i++08JPLzcLLMt+8J8=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ pkg-config ];
|
||||
buildInputs = [
|
||||
deadbeef
|
||||
gtk3
|
||||
sqlite
|
||||
];
|
||||
makeFlags = [ "gtk3" ];
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
mkdir -p $out/lib/deadbeef/
|
||||
install -v -c -m 644 gtk3/ddb_misc_waveform_GTK3.so $out/lib/deadbeef/
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "Waveform Seekbar plugin for DeaDBeeF audio player";
|
||||
homepage = "https://github.com/cboxdoerfer/ddb_waveform_seekbar";
|
||||
license = lib.licenses.gpl2Plus;
|
||||
platforms = lib.platforms.linux;
|
||||
maintainers = [ lib.maintainers.deudz ];
|
||||
};
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
qtbase,
|
||||
qtsvg,
|
||||
qttools,
|
||||
qtwayland,
|
||||
qwt,
|
||||
qscintilla,
|
||||
kissfftFloat,
|
||||
@@ -82,6 +83,7 @@ stdenv.mkDerivation rec {
|
||||
qtbase
|
||||
qtsvg
|
||||
qttools
|
||||
qtwayland
|
||||
qwt
|
||||
qscintilla
|
||||
kissfftFloat
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
}:
|
||||
mkLibretroCore {
|
||||
core = "mame2003-plus";
|
||||
version = "0-unstable-2025-05-16";
|
||||
version = "0-unstable-2025-06-28";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "libretro";
|
||||
repo = "mame2003-plus-libretro";
|
||||
rev = "c478eae7484b76aaacc76659dd4d7b8e1163bc87";
|
||||
hash = "sha256-l7GwSj7/A/1ZAAqWz1GtMDCl6F45GJqucDBD89yqcsU=";
|
||||
rev = "04fb75e4f1291a490574168f3a04f9455e4a008d";
|
||||
hash = "sha256-dMfLK47DojJwSvd7KMW0D0azgQalRW8mBJqYJHTA6ew=";
|
||||
};
|
||||
|
||||
makefile = "Makefile";
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
|
||||
buildGoModule (finalAttrs: {
|
||||
pname = "kubernetes-helm";
|
||||
version = "3.18.3";
|
||||
version = "3.18.4";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "helm";
|
||||
repo = "helm";
|
||||
rev = "v${finalAttrs.version}";
|
||||
sha256 = "sha256-V5gWzgsinT0hGFDocPlljH1ls8Z0j5cz37oPrB6LI9Y=";
|
||||
sha256 = "sha256-2xOrTguenFzX7rvwm1ojSqV6ARCUSPUs07y3ut9Teec=";
|
||||
};
|
||||
vendorHash = "sha256-r9DLYgEjxapUOAz+FCgYXqdE6APhGKO/YnshbLRmdrU=";
|
||||
vendorHash = "sha256-Z3OAbuoeAtChd9Sk4bbzgwIxmFrw+/1c4zyxpNP0xXg=";
|
||||
|
||||
subPackages = [ "cmd/helm" ];
|
||||
ldflags = [
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
|
||||
buildGoModule rec {
|
||||
pname = "helm-diff";
|
||||
version = "3.12.2";
|
||||
version = "3.12.3";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "databus23";
|
||||
repo = pname;
|
||||
repo = "helm-diff";
|
||||
rev = "v${version}";
|
||||
hash = "sha256-N7n9/MjZR9jCRk2iZmFBOxwn6GVUNuUBaQHJqcgOQc4=";
|
||||
hash = "sha256-zYeZwR8hDNYVJGdYjQNrQmyD5AwGcgyO7LzC5bd10k0=";
|
||||
};
|
||||
|
||||
vendorHash = "sha256-uJxLpbFKdH5t08wZD/zPS5UPZO7YPtiBqcdN6997ik4=";
|
||||
vendorHash = "sha256-rz5RZiKGM1AdDm4R2IPOYn///fKKmo96u2LROShFPJM=";
|
||||
|
||||
ldflags = [
|
||||
"-s"
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
Remove about:buildconfig. If used as-is, it would add unnecessary runtime dependencies.
|
||||
--- a/comm/mail/base/jar.mn
|
||||
+++ b/comm/mail/base/jar.mn
|
||||
@@ -132,8 +132,6 @@
|
||||
% override chrome://mozapps/content/profile/profileDowngrade.js chrome://messenger/content/profileDowngrade.js
|
||||
% override chrome://mozapps/content/profile/profileDowngrade.xhtml chrome://messenger/content/profileDowngrade.xhtml
|
||||
|
||||
-* content/messenger/buildconfig.html (content/buildconfig.html)
|
||||
-% override chrome://global/content/buildconfig.html chrome://messenger/content/buildconfig.html
|
||||
% override chrome://global/locale/appstrings.properties chrome://messenger/locale/appstrings.properties
|
||||
|
||||
comm.jar:
|
||||
@@ -47,7 +47,7 @@ let
|
||||
extraPatches =
|
||||
[
|
||||
# The file to be patched is different from firefox's `no-buildconfig-ffx90.patch`.
|
||||
./no-buildconfig.patch
|
||||
(if lib.versionOlder version "140" then ./no-buildconfig.patch else ./no-buildconfig-tb140.patch)
|
||||
]
|
||||
++ lib.optionals (lib.versionOlder version "139") [
|
||||
# clang-19 fixes for char_traits build issue
|
||||
@@ -95,8 +95,8 @@ rec {
|
||||
thunderbird = thunderbird-latest;
|
||||
|
||||
thunderbird-latest = common {
|
||||
version = "139.0.2";
|
||||
sha512 = "edb20c692674dc5c3ba70673f7dd03710bf7ac0ce2be614a7a4b3d2b40b20b4974aab2a621dd5b43720c412a590c08f8b78abeb9b61f288f3217c6a04cc1e8ff";
|
||||
version = "140.0";
|
||||
sha512 = "2e9a5fb44b21eba3e3295205142bfad666a65f9eea43118388968320597a940cf3c5675fbcf458fbbaa9e1bb85fe8a663feda6461b7e23f7103c5bb7a1103bd4";
|
||||
|
||||
updateScript = callPackage ./update.nix {
|
||||
attrPath = "thunderbirdPackages.thunderbird-latest";
|
||||
|
||||
@@ -39,11 +39,11 @@
|
||||
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "gnunet";
|
||||
version = "0.24.2";
|
||||
version = "0.24.3";
|
||||
|
||||
src = fetchurl {
|
||||
url = "mirror://gnu/gnunet/gnunet-${finalAttrs.version}.tar.gz";
|
||||
hash = "sha256-Lk5KkH2UJ/DD3U1nlczq9yzPOX6dyWH2DtvvMAb2r0c=";
|
||||
hash = "sha256-WwaJew6ESJu7Q4J47HPkNiRCsuBaY+QAI+wdDMzGxXY=";
|
||||
};
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
}:
|
||||
let
|
||||
pname = "fah-client";
|
||||
version = "8.3.18";
|
||||
version = "8.4.9";
|
||||
|
||||
cbangSrc = fetchFromGitHub {
|
||||
owner = "cauldrondevelopmentllc";
|
||||
repo = "cbang";
|
||||
rev = "bastet-v${version}";
|
||||
sha256 = "sha256-BQNomjz6Bhod3FOC5iICwt1rPrZgIxGQ08yspSvAnJc=";
|
||||
sha256 = "sha256-xApE5m8YyIFRJLQYeboWelWukuuIjHNZxPDyq0RzSL4=";
|
||||
};
|
||||
|
||||
fah-client = stdenv.mkDerivation {
|
||||
@@ -31,7 +31,7 @@ let
|
||||
owner = "FoldingAtHome";
|
||||
repo = "fah-client-bastet";
|
||||
rev = "v${version}";
|
||||
sha256 = "sha256-lqpC1fAMFb8iX02daVre/pE0c7DkwswlFigJS3ZGEjM=";
|
||||
sha256 = "sha256-PewXhmkTru2yJhMkenbn7pcmVsa7eomjrMvs1PUGph8=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
|
||||
@@ -60,34 +60,35 @@
|
||||
libvpl,
|
||||
qrcodegencpp,
|
||||
nix-update-script,
|
||||
extra-cmake-modules,
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib) optional optionals;
|
||||
|
||||
cef = cef-binary.overrideAttrs (oldAttrs: {
|
||||
version = "127.3.5";
|
||||
version = "138.0.17";
|
||||
__intentionallyOverridingVersion = true; # `cef-binary` uses the overridden `srcHash` values in its source FOD
|
||||
gitRevision = "114ea2a";
|
||||
chromiumVersion = "127.0.6533.120";
|
||||
gitRevision = "ac9b751";
|
||||
chromiumVersion = "138.0.7204.97";
|
||||
|
||||
srcHash =
|
||||
{
|
||||
aarch64-linux = "sha256-s8dR97rAO0mCUwbpYnPWyY3t8movq05HhZZKllhZdBs=";
|
||||
x86_64-linux = "sha256-57E7bZKpViWno9W4AaaSjL9B4uxq+rDXAou1tsiODUg=";
|
||||
aarch64-linux = "sha256-kdO7c9oUfv0HK8wTmvYzw9S6EapnSGEQNCGN9D1JSL0=";
|
||||
x86_64-linux = "sha256-3qgIhen6l/kxttyw0z78nmwox62riVhlmFSGPkUot7g=";
|
||||
}
|
||||
.${stdenv.hostPlatform.system} or (throw "unsupported system ${stdenv.hostPlatform.system}");
|
||||
});
|
||||
in
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "obs-studio";
|
||||
version = "31.0.4";
|
||||
version = "31.1.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "obsproject";
|
||||
repo = "obs-studio";
|
||||
rev = finalAttrs.version;
|
||||
hash = "sha256-YxBPVXin8oJlo++oJogY1WMamIJmRqtSmKZDBsIZPU4=";
|
||||
hash = "sha256-espGKoKldgjWoCEE+Xor7+N5N86HvWCf0V18tb8GaC8=";
|
||||
fetchSubmodules = true;
|
||||
};
|
||||
|
||||
@@ -107,6 +108,7 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
pkg-config
|
||||
wrapGAppsHook3
|
||||
wrapQtAppsHook
|
||||
extra-cmake-modules
|
||||
]
|
||||
++ optional scriptingSupport swig
|
||||
++ optional cudaSupport autoAddDriverRunpath;
|
||||
|
||||
@@ -3,39 +3,48 @@
|
||||
stdenvNoCC,
|
||||
mercurial,
|
||||
}:
|
||||
{
|
||||
name ? null,
|
||||
url,
|
||||
rev ? null,
|
||||
sha256 ? null,
|
||||
hash ? null,
|
||||
fetchSubrepos ? false,
|
||||
preferLocalBuild ? true,
|
||||
}:
|
||||
|
||||
if hash != null && sha256 != null then
|
||||
throw "Only one of sha256 or hash can be set"
|
||||
else
|
||||
# TODO: statically check if mercurial as the https support if the url starts with https.
|
||||
stdenvNoCC.mkDerivation {
|
||||
name = "hg-archive" + (lib.optionalString (name != null) "-${name}");
|
||||
builder = ./builder.sh;
|
||||
nativeBuildInputs = [ mercurial ];
|
||||
lib.extendMkDerivation {
|
||||
constructDrv = stdenvNoCC.mkDerivation;
|
||||
|
||||
impureEnvVars = lib.fetchers.proxyImpureEnvVars;
|
||||
extendDrvArgs =
|
||||
finalAttrs:
|
||||
{
|
||||
name ? null,
|
||||
url,
|
||||
rev ? null,
|
||||
sha256 ? null,
|
||||
hash ? null,
|
||||
fetchSubrepos ? false,
|
||||
preferLocalBuild ? true,
|
||||
}:
|
||||
# TODO: statically check if mercurial as the https support if the url starts with https.
|
||||
{
|
||||
name = "hg-archive" + (lib.optionalString (name != null) "-${name}");
|
||||
builder = ./builder.sh;
|
||||
nativeBuildInputs = [ mercurial ];
|
||||
|
||||
subrepoClause = lib.optionalString fetchSubrepos "S";
|
||||
impureEnvVars = lib.fetchers.proxyImpureEnvVars;
|
||||
|
||||
outputHashAlgo = if hash != null then null else "sha256";
|
||||
outputHashMode = "recursive";
|
||||
outputHash =
|
||||
if hash != null then
|
||||
hash
|
||||
else if sha256 != null then
|
||||
sha256
|
||||
else
|
||||
lib.fakeSha256;
|
||||
subrepoClause = lib.optionalString fetchSubrepos "S";
|
||||
|
||||
inherit url rev;
|
||||
inherit preferLocalBuild;
|
||||
}
|
||||
outputHashAlgo = if finalAttrs.hash != null && finalAttrs.hash != "" then null else "sha256";
|
||||
outputHashMode = "recursive";
|
||||
outputHash =
|
||||
lib.throwIf (finalAttrs.hash != null && sha256 != null) "Only one of sha256 or hash can be set"
|
||||
(
|
||||
if finalAttrs.hash != null then
|
||||
finalAttrs.hash
|
||||
else if sha256 != null then
|
||||
sha256
|
||||
else
|
||||
""
|
||||
);
|
||||
|
||||
inherit url rev hash;
|
||||
inherit preferLocalBuild;
|
||||
};
|
||||
|
||||
# No ellipsis
|
||||
inheritFunctionArgs = false;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
python3Packages.buildPythonApplication {
|
||||
pname = "accelergy";
|
||||
version = "unstable-2022-05-03";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "Accelergy-Project";
|
||||
@@ -16,11 +16,14 @@ python3Packages.buildPythonApplication {
|
||||
hash = "sha256-SRtt1EocHy5fKszpoumC+mOK/qhreoA2/Ff1wcu5WKo=";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = with python3Packages; [
|
||||
build-system = with python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3Packages; [
|
||||
pyyaml
|
||||
yamlordereddictloader
|
||||
pyfiglet
|
||||
setuptools
|
||||
];
|
||||
|
||||
meta = {
|
||||
|
||||
@@ -4,20 +4,21 @@
|
||||
fetchPypi,
|
||||
}:
|
||||
|
||||
with python3.pkgs;
|
||||
|
||||
buildPythonApplication rec {
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "adafruit-ampy";
|
||||
version = "1.1.0";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "f4cba36f564096f2aafd173f7fbabb845365cc3bb3f41c37541edf98b58d3976";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ setuptools-scm ];
|
||||
propagatedBuildInputs = [
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3.pkgs; [
|
||||
click
|
||||
python-dotenv
|
||||
pyserial
|
||||
|
||||
@@ -7,14 +7,18 @@
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "addic7ed-cli";
|
||||
version = "1.4.6";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "182cpwxpdybsgl1nps850ysvvjbqlnx149kri4hxhgm58nqq0qf5";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = with python3Packages; [
|
||||
build-system = with python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3Packages; [
|
||||
requests
|
||||
pyquery
|
||||
];
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "aiodnsbrute";
|
||||
version = "0.3.3";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "blark";
|
||||
@@ -16,7 +16,11 @@ python3.pkgs.buildPythonApplication rec {
|
||||
hash = "sha256-cEpk71VoQJZfKeAZummkk7yjtXKSMndgo0VleYiMlWE=";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = with python3.pkgs; [
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3.pkgs; [
|
||||
aiodns
|
||||
click
|
||||
tqdm
|
||||
|
||||
@@ -5,11 +5,10 @@
|
||||
ncurses,
|
||||
}:
|
||||
|
||||
with python3.pkgs;
|
||||
buildPythonApplication rec {
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "almonds";
|
||||
version = "1.25b";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "Tenchi2xh";
|
||||
@@ -18,11 +17,13 @@ buildPythonApplication rec {
|
||||
sha256 = "0j8d8jizivnfx8lpc4w6sbqj5hq35nfz0vdg7ld80sc5cs7jr3ws";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ pytest ];
|
||||
buildInputs = [ ncurses ];
|
||||
propagatedBuildInputs = [ pillow ];
|
||||
build-system = with python3.pkgs; [ setuptools ];
|
||||
|
||||
checkPhase = "py.test";
|
||||
dependencies = with python3.pkgs; [ pillow ];
|
||||
|
||||
buildInputs = [ ncurses ];
|
||||
|
||||
nativeCheckInputs = with python3.pkgs; [ pytestCheckHook ];
|
||||
|
||||
meta = with lib; {
|
||||
description = "Terminal Mandelbrot fractal viewer";
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "amoco";
|
||||
version = "2.9.8";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "bdcht";
|
||||
@@ -16,10 +16,11 @@ python3.pkgs.buildPythonApplication rec {
|
||||
hash = "sha256-3+1ssFyU7SKFJgDYBQY0kVjmTHOD71D2AjnH+4bfLXo=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = with python3.pkgs; [
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
propagatedBuildInputs = with python3.pkgs; [
|
||||
dependencies = with python3.pkgs; [
|
||||
blessed
|
||||
click
|
||||
crysp
|
||||
|
||||
@@ -13,7 +13,7 @@ let
|
||||
description = "Advanced typing practice program";
|
||||
in
|
||||
python3Packages.buildPythonApplication {
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
inherit pname version;
|
||||
|
||||
src = fetchFromGitLab {
|
||||
@@ -33,7 +33,11 @@ python3Packages.buildPythonApplication {
|
||||
qt5.qtwayland
|
||||
];
|
||||
|
||||
propagatedBuildInputs = with python3Packages; [
|
||||
build-system = with python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3Packages; [
|
||||
editdistance
|
||||
pyqt5
|
||||
translitcodec
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
python3Packages.buildPythonApplication {
|
||||
pname = "antfs-cli";
|
||||
version = "unstable-2017-02-11";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
meta = with lib; {
|
||||
homepage = "https://github.com/Tigge/antfs-cli";
|
||||
@@ -24,5 +24,7 @@ python3Packages.buildPythonApplication {
|
||||
sha256 = "0v8y64kldfbs809j1g9d75dd1vxq7mfxnp4b45pz8anpxhjf64fy";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = [ python3Packages.openant ];
|
||||
build-system = [ python3Packages.setuptools ];
|
||||
|
||||
dependencies = [ python3Packages.openant ];
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "apksigcopier";
|
||||
version = "1.1.1";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "obfusk";
|
||||
@@ -25,7 +25,11 @@ python3.pkgs.buildPythonApplication rec {
|
||||
pandoc
|
||||
];
|
||||
|
||||
propagatedBuildInputs = with python3.pkgs; [
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3.pkgs; [
|
||||
click
|
||||
];
|
||||
|
||||
@@ -38,7 +42,7 @@ python3.pkgs.buildPythonApplication rec {
|
||||
|
||||
postPatch = ''
|
||||
substituteInPlace Makefile \
|
||||
--replace /bin/bash ${bash}/bin/bash
|
||||
--replace-fail /bin/bash ${bash}/bin/bash
|
||||
'';
|
||||
|
||||
postBuild = ''
|
||||
|
||||
@@ -18,11 +18,15 @@ let
|
||||
};
|
||||
in
|
||||
python3Packages.buildPythonApplication {
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
inherit pname version src;
|
||||
|
||||
nativeBuildInputs = [ installShellFiles ];
|
||||
|
||||
build-system = with python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
postPatch = ''
|
||||
substituteInPlace org.debian.apt.aptoffline.policy \
|
||||
--replace-fail /usr/bin/ "$out/bin"
|
||||
|
||||
@@ -22,21 +22,23 @@ let
|
||||
};
|
||||
};
|
||||
in
|
||||
with py.pkgs;
|
||||
|
||||
buildPythonApplication rec {
|
||||
py.pkgs.buildPythonApplication rec {
|
||||
pname = "archivy";
|
||||
version = "1.7.3";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchPypi {
|
||||
inherit pname version;
|
||||
hash = "sha256-ns1Y0DqqnTAQMEt+oBJ/P2gqKqPsX9P3/Z4561qzuns";
|
||||
};
|
||||
|
||||
build-system = with py.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
pythonRelaxDeps = true;
|
||||
|
||||
propagatedBuildInputs = [
|
||||
dependencies = with py.pkgs; [
|
||||
appdirs
|
||||
attrs
|
||||
beautifulsoup4
|
||||
@@ -50,7 +52,7 @@ buildPythonApplication rec {
|
||||
python-frontmatter
|
||||
readability-lxml
|
||||
requests
|
||||
setuptools
|
||||
setuptools # uses pkg_resources during runtime
|
||||
tinydb
|
||||
validators
|
||||
wtforms
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "arsenal";
|
||||
version = "1.1.0";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "Orange-Cyberdefense";
|
||||
@@ -16,7 +16,11 @@ python3.pkgs.buildPythonApplication rec {
|
||||
sha256 = "sha256-NbNXyR5aNKvRJU9JWGk/ndwU1bhNgDOdcRqBkAY9nPA=";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = with python3.pkgs; [
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3.pkgs; [
|
||||
libtmux
|
||||
docutils
|
||||
pyfzf
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "arxiv-latex-cleaner";
|
||||
version = "1.0.8";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "google-research";
|
||||
@@ -16,7 +16,11 @@ python3Packages.buildPythonApplication rec {
|
||||
hash = "sha256-CQb1u1j+/px+vNqA3iXZ2oe6/0ZWeMjWrUQL9elRDEI=";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = with python3Packages; [
|
||||
build-system = with python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3Packages; [
|
||||
pillow
|
||||
pyyaml
|
||||
regex
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "asn1editor";
|
||||
version = "0.8.0";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "Futsch1";
|
||||
@@ -16,7 +16,11 @@ python3.pkgs.buildPythonApplication rec {
|
||||
hash = "sha256-mgluhC2DMS4OyS/BoWqBdVf7GcxquOtOKTHZ/hbiHQM=";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = with python3.pkgs; [
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3.pkgs; [
|
||||
asn1tools
|
||||
coverage
|
||||
wxpython
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
|
||||
buildGoModule (finalAttrs: {
|
||||
pname = "atlantis";
|
||||
version = "0.34.0";
|
||||
version = "0.35.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "runatlantis";
|
||||
repo = "atlantis";
|
||||
tag = "v${finalAttrs.version}";
|
||||
hash = "sha256-2xgU3H6X9EcbySV9RXN5oCn+7EkfdwebeYsL5+Vl69E=";
|
||||
hash = "sha256-mdUh/fJo4pA80++nJoYdiAS5oTPpvBsR0TIMHrQO4u8=";
|
||||
};
|
||||
|
||||
ldflags = [
|
||||
@@ -21,7 +21,7 @@ buildGoModule (finalAttrs: {
|
||||
"-X=main.date=1970-01-01T00:00:00Z"
|
||||
];
|
||||
|
||||
vendorHash = "sha256-1xII3GIQQCku3UzwPJnJu//zAJGuGCOSETR6sU4lPR8=";
|
||||
vendorHash = "sha256-QtAR0vO2K014WlzAriAYg7272tZ1iu72g4a3OBsy6Wo=";
|
||||
|
||||
subPackages = [ "." ];
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "atomic-operator";
|
||||
version = "0.8.5";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "swimlane";
|
||||
@@ -16,12 +16,16 @@ python3.pkgs.buildPythonApplication rec {
|
||||
hash = "sha256-DyNqu3vndyLkmfybCfTbgxk3t/ALg7IAkAMg4kBkH7Q=";
|
||||
};
|
||||
|
||||
postPatch = ''
|
||||
substituteInPlace setup.py \
|
||||
--replace "charset_normalizer~=2.0.0" "charset_normalizer"
|
||||
'';
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
propagatedBuildInputs = with python3.pkgs; [
|
||||
pythonRelaxDeps = [
|
||||
"charset_normalizer"
|
||||
"urllib3"
|
||||
];
|
||||
|
||||
dependencies = with python3.pkgs; [
|
||||
attrs
|
||||
certifi
|
||||
chardet
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "autokey";
|
||||
version = "0.96.0";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "autokey";
|
||||
@@ -37,7 +37,11 @@ python3Packages.buildPythonApplication rec {
|
||||
libnotify
|
||||
];
|
||||
|
||||
propagatedBuildInputs = with python3Packages; [
|
||||
build-system = with python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3Packages; [
|
||||
dbus-python
|
||||
pyinotify
|
||||
xlib
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "ayatana-webmail";
|
||||
version = "24.5.17";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "AyatanaIndicators";
|
||||
@@ -53,7 +53,11 @@ python3Packages.buildPythonApplication rec {
|
||||
glib # For compiling gsettings-schemas
|
||||
];
|
||||
|
||||
propagatedBuildInputs = with python3Packages; [
|
||||
build-system = with python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3Packages; [
|
||||
urllib3
|
||||
babel
|
||||
psutil
|
||||
|
||||
@@ -10,7 +10,7 @@ let
|
||||
in
|
||||
python3Packages.buildPythonApplication {
|
||||
inherit pname version;
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchPypi {
|
||||
inherit version;
|
||||
@@ -18,6 +18,10 @@ python3Packages.buildPythonApplication {
|
||||
hash = "sha256-UWS1weiccSGqBU8grPAUKkuXb7qs5wliHVaPgdW4KtI=";
|
||||
};
|
||||
|
||||
build-system = with python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
# If enabled, it will attempt to run '__init__.py, failing by trying to write
|
||||
# at "/homeless-shelter" as HOME
|
||||
doCheck = false;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
python3Packages.buildPythonApplication {
|
||||
pname = "bashplotlib";
|
||||
version = "2021-03-31";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "glamp";
|
||||
@@ -16,6 +16,10 @@ python3Packages.buildPythonApplication {
|
||||
sha256 = "sha256-0S6mgy6k7CcqsDR1kE5xcXGidF1t061e+M+ZuP2Gk3c=";
|
||||
};
|
||||
|
||||
build-system = with python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
# No tests
|
||||
doCheck = false;
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
lib,
|
||||
python3Packages,
|
||||
fetchFromGitHub,
|
||||
versionCheckHook,
|
||||
nix-update-script,
|
||||
}:
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "beeref";
|
||||
version = "0.3.3";
|
||||
pyproject = true;
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "rbreu";
|
||||
repo = "beeref";
|
||||
tag = "v${version}";
|
||||
hash = "sha256-GtxiJKj3tlzI1kVXzJg0LNAUcodXSna17ZvAtsAEH4M=";
|
||||
};
|
||||
|
||||
build-system = [ python3Packages.setuptools ];
|
||||
|
||||
dependencies = with python3Packages; [
|
||||
exif
|
||||
lxml
|
||||
pyqt6
|
||||
rectangle-packer
|
||||
];
|
||||
|
||||
pythonRelaxDeps = [
|
||||
"lxml"
|
||||
"pyqt6"
|
||||
"rectangle-packer"
|
||||
];
|
||||
|
||||
pythonRemoveDeps = [ "pyqt6-qt6" ];
|
||||
|
||||
pythonImportsCheck = [ "beeref" ];
|
||||
|
||||
# Tests fail with "Fatal Python error: Aborted" due to PyQt6 GUI initialization issues in sandbox
|
||||
# Only versionCheckHook and pythonImportsCheck are used for basic validation
|
||||
nativeCheckInputs = [ versionCheckHook ];
|
||||
|
||||
passthru.updateScript = nix-update-script { };
|
||||
|
||||
meta = {
|
||||
changelog = "https://github.com/rbreu/beeref/blob/v${version}/CHANGELOG.rst";
|
||||
description = "Reference image viewer";
|
||||
homepage = "https://beeref.org";
|
||||
license = with lib.licenses; [
|
||||
cc0
|
||||
gpl3Only
|
||||
];
|
||||
mainProgram = "beeref";
|
||||
maintainers = with lib.maintainers; [ HeitorAugustoLN ];
|
||||
platforms = lib.platforms.all;
|
||||
sourceProvenance = [ lib.sourceTypes.fromSource ];
|
||||
};
|
||||
}
|
||||
@@ -10,13 +10,13 @@
|
||||
}:
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "beidconnect";
|
||||
version = "2.10";
|
||||
version = "2.11";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "Fedict";
|
||||
repo = "fts-beidconnect";
|
||||
rev = finalAttrs.version;
|
||||
hash = "sha256-xkBldXOlgLMgrvzm7ajXzJ92mpXrxHD1RX4DeBxU3kk=";
|
||||
hash = "sha256-4eKO2yw2Ipfu1PvebgOR+BihsLlnWIJejGWqjztPA2I=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ pkg-config ];
|
||||
|
||||
@@ -10,17 +10,17 @@
|
||||
}:
|
||||
rustPlatform.buildRustPackage (finalAttrs: {
|
||||
pname = "biome";
|
||||
version = "2.0.0";
|
||||
version = "2.1.1";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "biomejs";
|
||||
repo = "biome";
|
||||
rev = "@biomejs/biome@${finalAttrs.version}";
|
||||
hash = "sha256-2oHEaHKTyD+j34Or/Obb0pPGpEXEgSq6wowyYVV6DqI=";
|
||||
hash = "sha256-ZnmMo3zUk+///avGQ497YNj9gChds4efpD88cjTr2JA=";
|
||||
};
|
||||
|
||||
useFetchCargoVendor = true;
|
||||
cargoHash = "sha256-jh7LlX7Ip2oy5NcXHfFkGeyJVGeu4Y0HqN690bok+/E=";
|
||||
cargoHash = "sha256-WIZrxQh83tebalDMa/2/d/+xFDe7uhpTf/Gmx1Kr55E=";
|
||||
|
||||
nativeBuildInputs = [ pkg-config ];
|
||||
|
||||
@@ -66,6 +66,7 @@ rustPlatform.buildRustPackage (finalAttrs: {
|
||||
maintainers = with lib.maintainers; [
|
||||
figsoda
|
||||
isabelroses
|
||||
wrbbz
|
||||
];
|
||||
mainProgram = "biome";
|
||||
};
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
fetchPypi,
|
||||
}:
|
||||
|
||||
with python3.pkgs;
|
||||
|
||||
buildPythonApplication rec {
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "bkyml";
|
||||
version = "1.4.3";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchPypi {
|
||||
inherit pname version;
|
||||
@@ -21,24 +19,27 @@ buildPythonApplication rec {
|
||||
# of the package, that has been affected by the pyscaffold package dependency removal.
|
||||
postPatch = ''
|
||||
substituteInPlace setup.py \
|
||||
--replace "['pyscaffold>=3.0a0,<3.1a0'] + " "" \
|
||||
--replace "use_pyscaffold=True" ""
|
||||
substituteInPlace src/bkyml/skeleton.py --replace \
|
||||
"from bkyml import __version__" \
|
||||
"__version__ = \"${version}\""
|
||||
--replace-fail "['pyscaffold>=3.0a0,<3.1a0'] + " "" \
|
||||
--replace-fail "use_pyscaffold=True" ""
|
||||
substituteInPlace src/bkyml/__init__.py \
|
||||
--replace-fail "from pkg_resources" "# from pkg_resources" \
|
||||
--replace-fail "get_distribution(dist_name).version" '"${version}"'
|
||||
'';
|
||||
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3.pkgs; [
|
||||
ruamel-yaml
|
||||
];
|
||||
|
||||
# Don't run tests because they are broken when run within
|
||||
# buildPythonApplication for reasons I don't quite understand.
|
||||
doCheck = false;
|
||||
|
||||
pythonImportsCheck = [ "bkyml" ];
|
||||
|
||||
propagatedBuildInputs = [
|
||||
ruamel-yaml
|
||||
setuptools
|
||||
];
|
||||
|
||||
meta = with lib; {
|
||||
homepage = "https://github.com/joscha/bkyml";
|
||||
description = "CLI tool to generate a pipeline.yaml file for Buildkite on the fly";
|
||||
|
||||
@@ -52,6 +52,7 @@ let
|
||||
"8.1" = "sha256-5EW9BkG154HQ6TrMyan5EhXiGlSRFPXMMTUasIwuC/U=";
|
||||
"8.2" = "sha256-hRjh9Bf04LVBtS08fWMxrE1iyn6SGBQfNNLuSyQPjes=";
|
||||
"8.3" = "sha256-WWsDPQhu1GXMDe6NhlMuVcwi7wGzRLJcJwxItxFCOiI=";
|
||||
"8.4" = "sha256-tRxQfrFJmyaGRTa5ZWXhSHLx3V6QcBGe0EzKYbOjmG8=";
|
||||
};
|
||||
};
|
||||
"x86_64-darwin" = {
|
||||
@@ -60,6 +61,7 @@ let
|
||||
"8.1" = "sha256-w56HItrNtHA8jj9K5LhGTKFRX5i9UYJpxVwR0eFQe4E=";
|
||||
"8.2" = "sha256-vkEAVyZ6Vs3VjWb3oNrlRz5zAzPbgIngeoDAHZLme3Q=";
|
||||
"8.3" = "sha256-uzobd13RzYGFrXHyFH0Ud9Qg7AWMPAA5dvHCp7R3HrU=";
|
||||
"8.4" = "sha256-PtMXo/NTV8E32b9aWuuxBoHeeFN3vP2ufEo1Ed0w2iI=";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "blivet-gui";
|
||||
version = "2.6.0";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "storaged-project";
|
||||
@@ -47,6 +47,10 @@ python3.pkgs.buildPythonApplication rec {
|
||||
|
||||
buildInputs = [ gtk3 ];
|
||||
|
||||
build-system = [
|
||||
python3.pkgs.setuptools
|
||||
];
|
||||
|
||||
dependencies = [
|
||||
python3.pkgs.blivet
|
||||
python3.pkgs.pyparted
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "brotab";
|
||||
version = "1.4.2";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "balta2ar";
|
||||
@@ -26,7 +26,11 @@ python3Packages.buildPythonApplication rec {
|
||||
})
|
||||
];
|
||||
|
||||
propagatedBuildInputs = with python3Packages; [
|
||||
build-system = with python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3Packages; [
|
||||
flask
|
||||
psutil
|
||||
requests
|
||||
@@ -35,9 +39,9 @@ python3Packages.buildPythonApplication rec {
|
||||
|
||||
postPatch = ''
|
||||
substituteInPlace requirements/base.txt \
|
||||
--replace "Flask==2.0.2" "Flask>=2.0.2" \
|
||||
--replace "psutil==5.8.0" "psutil>=5.8.0" \
|
||||
--replace "requests==2.24.0" "requests>=2.24.0"
|
||||
--replace-fail "Flask==2.0.2" "Flask>=2.0.2" \
|
||||
--replace-fail "psutil==5.8.0" "psutil>=5.8.0" \
|
||||
--replace-fail "requests==2.24.0" "requests>=2.24.0"
|
||||
'';
|
||||
|
||||
__darwinAllowLocalNetworking = true;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "btlejack";
|
||||
version = "2.1.1";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "virtualabs";
|
||||
@@ -20,7 +20,11 @@ python3Packages.buildPythonApplication rec {
|
||||
sed -i "s|^.*'argparse',$||" setup.py
|
||||
'';
|
||||
|
||||
propagatedBuildInputs = [
|
||||
build-system = [
|
||||
python3Packages.setuptools
|
||||
];
|
||||
|
||||
dependencies = [
|
||||
python3Packages.pyserial
|
||||
python3Packages.halo
|
||||
];
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "bubblemail";
|
||||
version = "1.9";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitLab {
|
||||
domain = "framagit.org";
|
||||
@@ -53,6 +53,10 @@ python3Packages.buildPythonApplication rec {
|
||||
gobject-introspection
|
||||
];
|
||||
|
||||
build-system = with python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
propagatedBuildInputs = with python3Packages; [
|
||||
gsettings-desktop-schemas
|
||||
pygobject3
|
||||
|
||||
@@ -108,13 +108,28 @@ stdenv.mkDerivation {
|
||||
dontConfigure = true;
|
||||
dontStrip = true;
|
||||
|
||||
unpackPhase = "unzstd ${buck2-src} -o ./buck2 && unzstd ${rust-project-src} -o ./rust-project";
|
||||
buildPhase = "chmod +x ./buck2 && chmod +x ./rust-project";
|
||||
checkPhase = "./buck2 --version && ./rust-project --version";
|
||||
unpackPhase = ''
|
||||
runHook preUnpack
|
||||
unzstd ${buck2-src} -o ./buck2
|
||||
unzstd ${rust-project-src} -o ./rust-project
|
||||
runHook postUnpack
|
||||
'';
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
chmod +x ./buck2 && chmod +x ./rust-project
|
||||
runHook postBuild
|
||||
'';
|
||||
checkPhase = ''
|
||||
runHook preCheck
|
||||
./buck2 --version && ./rust-project --version
|
||||
runHook postCheck
|
||||
'';
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p $out/bin
|
||||
install -D buck2 $out/bin/buck2
|
||||
install -D rust-project $out/bin/rust-project
|
||||
runHook postInstall
|
||||
'';
|
||||
postInstall = lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) ''
|
||||
installShellCompletion --cmd buck2 \
|
||||
@@ -149,7 +164,11 @@ stdenv.mkDerivation {
|
||||
mit
|
||||
];
|
||||
mainProgram = "buck2";
|
||||
maintainers = with lib.maintainers; [ thoughtpolice ];
|
||||
maintainers = with lib.maintainers; [
|
||||
thoughtpolice
|
||||
lf-
|
||||
_9999years
|
||||
];
|
||||
platforms = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
|
||||
@@ -22,7 +22,7 @@ in
|
||||
python3.pkgs.buildPythonPackage {
|
||||
pname = "bumblebee-status";
|
||||
inherit version;
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "tobi-wan-kenobi";
|
||||
@@ -40,10 +40,15 @@ python3.pkgs.buildPythonPackage {
|
||||
})
|
||||
];
|
||||
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
buildInputs = lib.concatMap (p: p.buildInputs or [ ]) selectedPlugins;
|
||||
|
||||
propagatedBuildInputs = lib.concatMap (p: p.propagatedBuildInputs or [ ]) selectedPlugins;
|
||||
|
||||
checkInputs = with python3.pkgs; [
|
||||
nativeCheckInputs = with python3.pkgs; [
|
||||
freezegun
|
||||
netifaces
|
||||
psutil
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "bumpver";
|
||||
version = "2021.1110";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchPypi {
|
||||
inherit pname version;
|
||||
@@ -18,12 +18,16 @@ python3.pkgs.buildPythonApplication rec {
|
||||
|
||||
prePatch = ''
|
||||
substituteInPlace setup.py \
|
||||
--replace "if any(arg.startswith(\"bdist\") for arg in sys.argv):" ""\
|
||||
--replace "import lib3to6" ""\
|
||||
--replace "package_dir = lib3to6.fix(package_dir)" ""
|
||||
--replace-fail "if any(arg.startswith(\"bdist\") for arg in sys.argv):" ""\
|
||||
--replace-fail "import lib3to6" ""\
|
||||
--replace-fail "package_dir = lib3to6.fix(package_dir)" ""
|
||||
'';
|
||||
|
||||
propagatedBuildInputs = with python3.pkgs; [
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3.pkgs; [
|
||||
pathlib2
|
||||
click
|
||||
toml
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
python3Packages.buildPythonApplication rec {
|
||||
version = "0.9.9";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
pname = "canto-curses";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
@@ -28,6 +28,10 @@ python3Packages.buildPythonApplication rec {
|
||||
})
|
||||
];
|
||||
|
||||
build-system = with python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
readline
|
||||
ncurses
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
python3Packages.buildPythonApplication rec {
|
||||
version = "0.9.8";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
pname = "canto-daemon";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
@@ -16,7 +16,9 @@ python3Packages.buildPythonApplication rec {
|
||||
sha256 = "0fmsdn28z09bvivdkqcla5bnalky7k744iir25z70bv4pz1jcvnk";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = with python3Packages; [ feedparser ];
|
||||
build-system = with python3Packages; [ setuptools ];
|
||||
|
||||
dependencies = with python3Packages; [ feedparser ];
|
||||
|
||||
doCheck = false;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "cantoolz";
|
||||
version = "3.7.0";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "CANToolz";
|
||||
@@ -30,7 +30,11 @@ python3.pkgs.buildPythonApplication rec {
|
||||
})
|
||||
];
|
||||
|
||||
propagatedBuildInputs = with python3.pkgs; [
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3.pkgs; [
|
||||
flask
|
||||
pyserial
|
||||
mido
|
||||
|
||||
@@ -13,17 +13,17 @@
|
||||
|
||||
rustPlatform.buildRustPackage rec {
|
||||
pname = "tauri";
|
||||
version = "2.5.0";
|
||||
version = "2.6.2";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "tauri-apps";
|
||||
repo = "tauri";
|
||||
tag = "tauri-cli-v${version}";
|
||||
hash = "sha256-ut5Etn5yf4X3NvFa5JCRH2sQGnC/xzaRhALoyxdjy2k=";
|
||||
hash = "sha256-QdboIHbRKC/0k6FGKDuCA7AR3eIa7KVij3fGekD9kNk=";
|
||||
};
|
||||
|
||||
useFetchCargoVendor = true;
|
||||
cargoHash = "sha256-1YLpK2frSmdCj5aksuZhnHkAZdwHX/ZuVKXyqVJel/s=";
|
||||
cargoHash = "sha256-GFqUQLLURfm6sRpf4MwAp89aKpTwWIlxk3NNRf9QgC0=";
|
||||
|
||||
nativeBuildInputs = [ pkg-config ];
|
||||
|
||||
|
||||
@@ -7,16 +7,17 @@
|
||||
libcap,
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "cdrtools";
|
||||
version = "3.02a09";
|
||||
|
||||
src = fetchurl {
|
||||
url = "mirror://sourceforge/cdrtools/${pname}-${version}.tar.bz2";
|
||||
sha256 = "10ayj48jax2pvsv6j5gybwfsx7b74zdjj84znwag7wwf8n7l6a5a";
|
||||
url = "mirror://sourceforge/cdrtools/cdrtools-${finalAttrs.version}.tar.bz2";
|
||||
hash = "sha256-qihDj0WO8/MUt58gKdsnZ52uHV/+FWm23ld0JRGRXoE=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ m4 ];
|
||||
|
||||
buildInputs = lib.optionals (!stdenv.hostPlatform.isDarwin) [
|
||||
acl
|
||||
libcap
|
||||
@@ -57,19 +58,19 @@ stdenv.mkDerivation rec {
|
||||
|
||||
hardeningDisable = lib.optional stdenv.hostPlatform.isMusl "fortify";
|
||||
|
||||
meta = with lib; {
|
||||
meta = {
|
||||
homepage = "https://cdrtools.sourceforge.net/private/cdrecord.html";
|
||||
description = "Highly portable CD/DVD/BluRay command line recording software";
|
||||
license = with licenses; [
|
||||
license = with lib.licenses; [
|
||||
cddl
|
||||
gpl2Plus
|
||||
lgpl21
|
||||
lgpl21Plus
|
||||
];
|
||||
maintainers = with maintainers; [ wegank ];
|
||||
platforms = with platforms; linux ++ darwin;
|
||||
maintainers = with lib.maintainers; [ wegank ];
|
||||
platforms = with lib.platforms; linux ++ darwin;
|
||||
# Licensing issues: This package contains code licensed under CDDL, GPL2
|
||||
# and LGPL2. There is a debate regarding the legality of distributing this
|
||||
# package in binary form.
|
||||
hydraPlatforms = [ ];
|
||||
};
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
lib,
|
||||
fetchFromGitHub,
|
||||
python3,
|
||||
}:
|
||||
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "certmitm";
|
||||
version = "0-unstable-2025-05-14";
|
||||
pyproject = false;
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "aapooksman";
|
||||
repo = "certmitm";
|
||||
rev = "f04c91a1ba231762ec126487f593f9b4d33f4ec2";
|
||||
hash = "sha256-i4DnOyn56lA63hI40uxtXX8dzMa29BPQQHWKFdVjVlM=";
|
||||
};
|
||||
|
||||
dependencies = with python3.pkgs; [
|
||||
dpkt
|
||||
pyopenssl
|
||||
];
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
install -vD certmitm.py $out/bin/certmitm
|
||||
install -vd $out/${python3.sitePackages}/
|
||||
cp -R certmitm $out/${python3.sitePackages}
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
# Project has not tests
|
||||
doCheck = false;
|
||||
|
||||
meta = {
|
||||
description = "Tool for testing for certificate validation vulnerabilities of TLS connections";
|
||||
homepage = "https://github.com/aapooksman/certmitm";
|
||||
license = lib.licenses.mit;
|
||||
maintainers = with lib.maintainers; [ fab ];
|
||||
mainProgram = "certmitm";
|
||||
platforms = lib.platforms.all;
|
||||
};
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "charge-lnd";
|
||||
version = "0.3.0";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "accumulator";
|
||||
@@ -17,6 +17,10 @@ python3Packages.buildPythonApplication rec {
|
||||
hash = "sha256-a/zIEA2oF1+BoZXk4YDWx69eVFSnANUE/F+ARI/VsXU=";
|
||||
};
|
||||
|
||||
build-system = with python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
propagatedBuildInputs = with python3Packages; [
|
||||
aiorpcx
|
||||
colorama
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "chatblade";
|
||||
version = "0.7.0";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchPypi {
|
||||
inherit pname version;
|
||||
@@ -17,7 +17,14 @@ python3Packages.buildPythonApplication rec {
|
||||
doCheck = false; # there are no tests
|
||||
|
||||
pythonImportsCheck = [ "chatblade" ];
|
||||
propagatedBuildInputs = with python3Packages; [
|
||||
|
||||
build-system = with python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
pythonRelaxDeps = true;
|
||||
|
||||
dependencies = with python3Packages; [
|
||||
openai
|
||||
platformdirs
|
||||
pylatexenc
|
||||
|
||||
Generated
+554
-324
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -6,13 +6,13 @@
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-code": "^1.0.43"
|
||||
"@anthropic-ai/claude-code": "^1.0.44"
|
||||
}
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-code": {
|
||||
"version": "1.0.43",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-1.0.43.tgz",
|
||||
"integrity": "sha512-VnuRK4s/R9ZRTkwH4gUjsp4SiBQXq7Y0B47OtgeXIZYVQYkhTW8m+E0IisFzXXFIyTQrE0SodGCpvgLhAYzGCg==",
|
||||
"version": "1.0.44",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-1.0.44.tgz",
|
||||
"integrity": "sha512-GCX0KeMcyhLlfs/dLWlMiHShAMmjt8d7xcVUS53z7VnV6s3cIIrRPsKQ/xX/Q9rFm5dSVmRnzU88Ku28fb3QKQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "SEE LICENSE IN README.md",
|
||||
"bin": {
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
|
||||
buildNpmPackage rec {
|
||||
pname = "claude-code";
|
||||
version = "1.0.43";
|
||||
version = "1.0.44";
|
||||
|
||||
nodejs = nodejs_20; # required for sandboxed Nix builds on Darwin
|
||||
|
||||
src = fetchzip {
|
||||
url = "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-${version}.tgz";
|
||||
hash = "sha256-MPnctLow88Muzd9h5c6w/u0tO4Umrl6YJcp/1/BTFD4=";
|
||||
hash = "sha256-Dnooy0KNfhirTu7hv6DfwL7SHwf++CKtG8VHptNhcxU=";
|
||||
};
|
||||
|
||||
npmDepsHash = "sha256-jwT+W/oithQ0AHpFmmD3E6XIBZ5VdCHz61RstOhVFHQ=";
|
||||
npmDepsHash = "sha256-Q3m4q0g/H5ZWmnMXSipRt3FUFu+SgDAJutVelQsv9ls=";
|
||||
|
||||
postPatch = ''
|
||||
cp ${./package-lock.json} package-lock.json
|
||||
@@ -28,9 +28,11 @@ buildNpmPackage rec {
|
||||
|
||||
# `claude-code` tries to auto-update by default, this disables that functionality.
|
||||
# https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview#environment-variables
|
||||
# The DEV=true env var causes claude to crash with `TypeError: window.WebSocket is not a constructor`
|
||||
postInstall = ''
|
||||
wrapProgram $out/bin/claude \
|
||||
--set DISABLE_AUTOUPDATER 1
|
||||
--set DISABLE_AUTOUPDATER 1 \
|
||||
--unset DEV
|
||||
'';
|
||||
|
||||
passthru.updateScript = ./update.sh;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "clickable";
|
||||
version = "8.3.1";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitLab {
|
||||
owner = "clickable";
|
||||
@@ -18,7 +18,9 @@ python3Packages.buildPythonApplication rec {
|
||||
hash = "sha256-Vn2PyALaRrE+jJRdZzW+jjCm3f2GfpgrQcFGB7kr4EM=";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = with python3Packages; [
|
||||
build-system = [ python3Packages.setuptools ];
|
||||
|
||||
dependencies = with python3Packages; [
|
||||
cookiecutter
|
||||
requests
|
||||
pyyaml
|
||||
|
||||
@@ -6,18 +6,18 @@
|
||||
|
||||
buildGoModule rec {
|
||||
pname = "cnspec";
|
||||
version = "11.61.0";
|
||||
version = "11.62.1";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "mondoohq";
|
||||
repo = "cnspec";
|
||||
tag = "v${version}";
|
||||
hash = "sha256-34dbAWpxQkvo4fwHAu9f5e8IQJ7wwgdnqI9NApkHi3A=";
|
||||
hash = "sha256-j31vx0ciyursGB9Ty0AStCbgSCBTyRkPPhCOiF+aTxs=";
|
||||
};
|
||||
|
||||
proxyVendor = true;
|
||||
|
||||
vendorHash = "sha256-dK1j9RRW7AYGvaO0saz8ZS//p/EJvIixOy/j8vSTtrY=";
|
||||
vendorHash = "sha256-qca0zdPkRQD+9QA6uz3Kl7UfVii+QOwlyz+SnORXd18=";
|
||||
|
||||
subPackages = [ "apps/cnspec" ];
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
|
||||
stdenvNoCC.mkDerivation (finalAttrs: {
|
||||
pname = "coconutbattery";
|
||||
version = "4.0.2,152";
|
||||
version = "4.0.4,166";
|
||||
|
||||
src = fetchzip {
|
||||
url = "https://coconut-flavour.com/downloads/coconutBattery_${
|
||||
lib.replaceStrings [ "." "," ] [ "" "_" ] finalAttrs.version
|
||||
}.zip";
|
||||
hash = "sha256-PNSDUp07lUx5ebcfM3WSJAfRQjeuIIy7KfY0KJ0i1AE=";
|
||||
hash = "sha256-ZbxO6pR752pjaBocA/wqyjPCZaUxV051MaHz1gqQjSg=";
|
||||
};
|
||||
|
||||
installPhase = ''
|
||||
|
||||
@@ -7,17 +7,17 @@
|
||||
|
||||
rustPlatform.buildRustPackage (finalAttrs: {
|
||||
pname = "codebook";
|
||||
version = "0.3.3";
|
||||
version = "0.3.5";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "blopker";
|
||||
repo = "codebook";
|
||||
tag = "v${finalAttrs.version}";
|
||||
hash = "sha256-9tXzruyISC+JdzV4aPBB31OCKgZVAO0eU3SsgAZy/+I=";
|
||||
hash = "sha256-lQfk4dJ9WFraxMDWJVSBiTGumikfHYlMBe+0NHa/3nY=";
|
||||
};
|
||||
|
||||
buildAndTestSubdir = "crates/codebook-lsp";
|
||||
cargoHash = "sha256-Bba5v0J5HRaylQRHV41LQ2My0zYybme/AHZ+HDekoHc=";
|
||||
cargoHash = "sha256-MLd7V5Pp8yx4pFAXSjZf4KUGp964ombrnGKbrtXhC0I=";
|
||||
|
||||
# Integration tests require internet access for dictionaries
|
||||
doCheck = false;
|
||||
|
||||
@@ -7,14 +7,18 @@
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "colorz";
|
||||
version = "1.0.3";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchPypi {
|
||||
inherit pname version;
|
||||
sha256 = "0ghd90lgplf051fs5n5bb42zffd3fqpgzkbv6bhjw7r8jqwgcky0";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = with python3Packages; [
|
||||
build-system = with python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3Packages; [
|
||||
pillow
|
||||
scipy
|
||||
];
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "compdb";
|
||||
version = "0.2.0";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "Sarcasm";
|
||||
@@ -16,6 +16,10 @@ python3.pkgs.buildPythonApplication rec {
|
||||
sha256 = "sha256-nFAgTrup6V5oE+LP4UWDOCgTVCv2v9HbQbkGW+oDnTg=";
|
||||
};
|
||||
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
meta = with lib; {
|
||||
description = "Command line tool to manipulate compilation databases";
|
||||
license = licenses.mit;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "cp210x-program";
|
||||
version = "0.4.1";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "VCTLabs";
|
||||
@@ -16,7 +16,11 @@ python3.pkgs.buildPythonApplication rec {
|
||||
sha256 = "sha256-IjKshP12WfFly9cPm6svD4qZW6cT8C7lOVrGenSqbfY=";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = with python3.pkgs; [
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3.pkgs; [
|
||||
hexdump
|
||||
pyusb
|
||||
];
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
python3Packages,
|
||||
}:
|
||||
|
||||
with python3Packages;
|
||||
|
||||
buildPythonApplication rec {
|
||||
python3Packages.buildPythonApplication rec {
|
||||
pname = "cppclean";
|
||||
version = "0.13";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "myint";
|
||||
@@ -22,6 +20,10 @@ buildPythonApplication rec {
|
||||
patchShebangs .
|
||||
'';
|
||||
|
||||
build-system = with python3Packages; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
checkPhase = ''
|
||||
./test.bash
|
||||
'';
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "cpuset";
|
||||
version = "1.6.2";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "lpechacek";
|
||||
@@ -16,6 +16,10 @@ python3.pkgs.buildPythonApplication rec {
|
||||
hash = "sha256-fW0SXNI10pb6FTn/2TOqxP9qlys0KL/H9m//NjslUaY=";
|
||||
};
|
||||
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
makeFlags = [ "prefix=$(out)" ];
|
||||
|
||||
checkPhase = ''
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "creds";
|
||||
version = "0.5.3";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "ihebski";
|
||||
@@ -21,10 +21,14 @@ python3.pkgs.buildPythonApplication rec {
|
||||
|
||||
postPatch = ''
|
||||
substituteInPlace creds \
|
||||
--replace "pathlib.Path(__file__).parent" "pathlib.Path.home()"
|
||||
--replace-fail "pathlib.Path(__file__).parent" "pathlib.Path.home()"
|
||||
'';
|
||||
|
||||
propagatedBuildInputs = with python3.pkgs; [
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3.pkgs; [
|
||||
fire
|
||||
prettytable
|
||||
requests
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "crlfsuite";
|
||||
version = "2.5.2";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "Nefcore";
|
||||
@@ -16,7 +16,11 @@ python3.pkgs.buildPythonApplication rec {
|
||||
sha256 = "sha256-mK20PbVGhTEjhY5L6coCzSMIrG/PHHmNq30ZoJEs6uI=";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = with python3.pkgs; [
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
dependencies = with python3.pkgs; [
|
||||
colorama
|
||||
requests
|
||||
];
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
python3Packages.buildPythonApplication {
|
||||
pname = "crowbar";
|
||||
version = "unstable-2020-04-23";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "galkan";
|
||||
@@ -20,7 +20,9 @@ python3Packages.buildPythonApplication {
|
||||
sha256 = "05m9vywr9976pc7il0ak8nl26mklzxlcqx0p8rlfyx1q766myqzf";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = [ python3Packages.paramiko ];
|
||||
build-system = [ python3Packages.setuptools ];
|
||||
|
||||
dependencies = [ python3Packages.paramiko ];
|
||||
|
||||
patchPhase = ''
|
||||
sed -i 's,/usr/bin/xfreerdp,${freerdp}/bin/xfreerdp,g' lib/main.py
|
||||
|
||||
@@ -7,12 +7,17 @@
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "csv2odf";
|
||||
version = "2.09";
|
||||
format = "setuptools";
|
||||
format = "pyproject";
|
||||
|
||||
src = fetchurl {
|
||||
url = "mirror://sourceforge/project/${pname}/${pname}-${version}/${pname}-${version}.tar.gz";
|
||||
sha256 = "09l0yfay89grjdzap2h11f0hcyn49np5zizg2yyp2aqgjs8ki57p";
|
||||
};
|
||||
|
||||
build-system = with python3.pkgs; [
|
||||
setuptools
|
||||
];
|
||||
|
||||
meta = with lib; {
|
||||
homepage = "https://sourceforge.net/p/csv2odf/wiki/Main_Page/";
|
||||
description = "Convert csv files to OpenDocument Format";
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user