Merge 686386f4cc into haskell-updates

This commit is contained in:
nixpkgs-ci[bot]
2025-07-10 00:22:36 +00:00
committed by GitHub
369 changed files with 15633 additions and 10653 deletions
+3
View File
@@ -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
View File
@@ -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:
+4
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
node_modules
+1
View File
@@ -0,0 +1 @@
package-lock-only = true
+4
View File
@@ -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".
+463
View File
@@ -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)
}
}
+1897
View File
File diff suppressed because it is too large Load Diff
+9
View File
@@ -0,0 +1,9 @@
{
"private": true,
"type": "module",
"dependencies": {
"@actions/artifact": "2.3.2",
"@actions/github": "6.0.1",
"bottleneck": "2.19.5"
}
}
+45
View File
@@ -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 })
}
+23
View File
@@ -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;
};
}
) { }
+1 -1
View File
@@ -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
View File
@@ -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.
+17
View File
@@ -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";
+1 -1
View File
@@ -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 = {
+5 -21
View File
@@ -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
View File
@@ -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
View File
@@ -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")
'';
}
+70 -67
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
import ./make-test-python.nix {
{
name = "mediatomb";
nodes = {
+73 -76
View File
@@ -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")
'';
}
+57 -64
View File
@@ -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
View File
@@ -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 = ''
+1 -1
View File
@@ -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
View File
@@ -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 ")
'';
}
);
}
+10 -7
View File
@@ -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;
+43 -46
View File
@@ -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/"
)
'';
}
+4 -9
View File
@@ -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\"'"
)
'';
}
+76 -92
View File
@@ -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\"'"
)
'';
}
+142 -164
View File
@@ -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;
+40 -31
View File
@@ -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;
}
+6 -3
View File
@@ -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 = {
+7 -6
View File
@@ -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
+6 -2
View File
@@ -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
];
+6 -2
View File
@@ -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
+8 -7
View File
@@ -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";
+4 -3
View File
@@ -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
+6 -2
View File
@@ -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
+4 -2
View File
@@ -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 ];
}
+7 -3
View File
@@ -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 = ''
+5 -1
View File
@@ -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"
+8 -6
View File
@@ -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
+6 -2
View File
@@ -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
+6 -2
View File
@@ -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
+3 -3
View File
@@ -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 = [ "." ];
+10 -6
View File
@@ -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
+6 -2
View File
@@ -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
+6 -2
View File
@@ -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;
+5 -1
View File
@@ -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;
+58
View File
@@ -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 ];
};
}
+2 -2
View File
@@ -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 ];
+4 -3
View File
@@ -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";
};
+15 -14
View File
@@ -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";
+2
View File
@@ -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=";
};
};
};
+5 -1
View File
@@ -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
+9 -5
View File
@@ -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;
+6 -2
View File
@@ -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
];
+5 -1
View File
@@ -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
+23 -4
View File
@@ -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"
+7 -2
View File
@@ -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 -5
View File
@@ -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
+5 -1
View File
@@ -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
+4 -2
View File
@@ -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;
+6 -2
View File
@@ -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
+3 -3
View File
@@ -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 = [ ];
};
}
})
+45
View File
@@ -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;
};
}
+5 -1
View File
@@ -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
+9 -2
View File
@@ -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
+554 -324
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -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": {
+6 -4
View File
@@ -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;
+4 -2
View File
@@ -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
+3 -3
View File
@@ -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" ];
+2 -2
View File
@@ -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 = ''
+3 -3
View File
@@ -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;
+6 -2
View File
@@ -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
];
+5 -1
View File
@@ -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;
+6 -2
View File
@@ -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
];
+6 -4
View File
@@ -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
'';
+5 -1
View File
@@ -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 -3
View File
@@ -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
+6 -2
View File
@@ -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
];
+4 -2
View File
@@ -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
+6 -1
View File
@@ -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