gitoriaLog in with ident

tickets

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Commit38f9d10f38f9d10ftickets: Hybriel master 06617221 (plugin allocators 3a781359 + 413f60e4, mpackdb 2cb7ae5e, http1 773de63e); gate 249/0, connect 60/0mre38f9d10f/migrate.hl

4.8 KB

  1. // migrate.hl — THE MIGRATION OF TICKET #20 (projects as own data, members, new states). Statics only.
  2. // Idempotent: it looks at the SHAPE of the data, so a second run (and every start of the app, project.hl
  3. // calls migrateAll) changes nothing. What it does to a store from before:
  4. // 1. a project record `{ name, created }` becomes `{ title = name, slug = name, description = '', oldSlugs = '' }`
  5. // 2. every ticket points at its project by the project's id (`project`, `path` = '<id>/<number>'), not by name;
  6. // `createdBy` = the user of its 'created' event; the state gets its new name (awaiting creator → review,
  7. // a "Question…" ticket → pending; confirmed → done, rejected → reopened, in progress → progress, on hold →
  8. // pending, anything unknown → open); a ticket in review / pending is ASSIGNED to the creator's user
  9. // 3. the state events of the history get the new names too
  10. // 4. a project without members gets: the creator (TICKETS_CREATOR_IDENTITY, users.hl) as admin; the users
  11. // named Architect and AntColonyScheduler that wrote in it as edit; everybody else who wrote in it as use.
  12. // A project with no admin at all gets the creator as admin as soon as the creator is known.
  13. // TICKETS_CREATOR_IDENTITY is read ONLY here (the creator-only workflow is gone).
  14. import { projectsTable, membersTable, ticketsTable, eventsTable, addMember, projectRecord, projectBySlug, projectRecords, memberRows, adminCount, stateOf, merged, countOf, first, roleOf } from './store.hl'
  15. import { ensureCreatorUser, usersTable } from './users.hl'
  16. import { now } from 'hl:time'
  17. static botNames = ['Architect' 'AntColonyScheduler']
  18. static migrateProjects = (&log) => {
  19. let all = projectsTable.find(null, null)
  20. if (countOf(all) == 0) { return 0 }
  21. let n = 0
  22. for (p of all) {
  23. if (p.slug == null) {
  24. projectsTable.update(p.id, { id = p.id title = p.name slug = p.name description = '' oldSlugs = '' created = p.created })
  25. n = n + 1
  26. }
  27. }
  28. if (n > 0) { log.push(n + ' project(s) turned into records') }
  29. return n
  30. }
  31. static createdByOf = (t) => {
  32. let all = eventsTable.find('ticket', t.id)
  33. if (countOf(all) == 0) { return '' }
  34. for (e of all) { if (e.kind == 'created' && e.user != null) { return e.user } }
  35. return ''
  36. }
  37. static migrateTickets = (&log, creator) => {
  38. let all = ticketsTable.find(null, null)
  39. if (countOf(all) == 0) { return 0 }
  40. let n = 0
  41. for (t of all) {
  42. if (projectRecord(t.project) == null) {
  43. let p = projectBySlug(t.project)
  44. if (p == null) {
  45. let pid = projectsTable.put({ title = t.project slug = t.project description = '' oldSlugs = '' created = now() })
  46. p = projectRecord(pid)
  47. }
  48. let s = stateOf(t.state)
  49. if (t.state == 'awaiting creator' && t.subject != null && t.subject.startsWith('Question')) { s = 'pending' }
  50. if (s == null) { s = 'open' }
  51. let who = ''
  52. if ((s == 'review' || s == 'pending') && creator != null) { who = creator.id }
  53. ticketsTable.update(t.id, merged(t, { project = p.id path = p.id + '/' + t.number state = s createdBy = createdByOf(t) assignee = who }))
  54. n = n + 1
  55. }
  56. }
  57. if (n > 0) { log.push(n + ' ticket(s) linked to their project by id, states renamed') }
  58. return n
  59. }
  60. static migrateEvents = (&log) => {
  61. let all = eventsTable.find(null, null)
  62. if (countOf(all) == 0) { return 0 }
  63. let n = 0
  64. for (e of all) {
  65. if (e.kind == 'state') {
  66. let a = stateOf(e.from)
  67. let b = stateOf(e.to)
  68. let nf = a != null ? a : e.from
  69. let nt = b != null ? b : e.to
  70. if (nf != e.from || nt != e.to) {
  71. eventsTable.update(e.id, merged(e, { from = nf to = nt }))
  72. n = n + 1
  73. }
  74. }
  75. }
  76. if (n > 0) { log.push(n + ' state event(s) renamed') }
  77. return n
  78. }
  79. static migrateMembers = (&log, creator) => {
  80. let n = 0
  81. for (p of projectRecords()) {
  82. if (countOf(membersTable.find('project', p.id)) == 0) {
  83. // who wrote in the project: users @id → true
  84. let wrote = {}
  85. let order = []
  86. let tickets = ticketsTable.find('project', p.id)
  87. if (countOf(tickets) > 0) {
  88. for (t of tickets) {
  89. let evs = eventsTable.find('ticket', t.id)
  90. if (countOf(evs) > 0) {
  91. for (e of evs) {
  92. if (e.user != null && e.user != '' && wrote[e.user] != true) {
  93. wrote[e.user] = true
  94. order.push(e.user)
  95. }
  96. }
  97. }
  98. }
  99. }
  100. for (uid of order) {
  101. let u = usersTable.fetch(uid)
  102. if (u != null) { addMember(p.id, uid, botNames.includes(u.name) ? 'edit' : 'use') }
  103. }
  104. n = n + 1
  105. }
  106. if (creator != null && adminCount(p.id) == 0) {
  107. addMember(p.id, creator.id, 'admin')
  108. n = n + 1
  109. }
  110. }
  111. if (n > 0) { log.push('members set up in ' + n + ' project(s)') }
  112. return n
  113. }
  114. // answers the lines describing what changed (an empty list = nothing to do)
  115. static migrateAll = () => {
  116. let log = []
  117. let creator = ensureCreatorUser()
  118. migrateProjects(log)
  119. migrateTickets(log, creator)
  120. migrateEvents(log)
  121. migrateMembers(log, creator)
  122. return log
  123. }

Branches

Latest commits

  • 38f9d10ftickets: Hybriel master 06617221 (plugin allocators 3a781359 + 413f60e4, mpackdb 2cb7ae5e, http1 773de63e); gate 249/0, connect 60/0mre
  • d3db6139tickets: Hybriel master 190aa11d (fc838894 GC correctness, #126 closure scopes, #127); gate 249/0, connect 60/0mre
  • bce182e3tickets: Hybriel master 7eea0d32 (#126 memory, #48 lambda copies its argument); migrate.hl lambdas take &logmre
  • 4137be0fantcolony#40: mission references point to the moved missionsmre
  • 9bfba36aantcolony#40: history (LOG.md), worker briefs (missions/) and reports moved here from antcolony, numbered per project; old numbers in antcolony docs/mission-map.mdmre
  • c7bd2645tickets: Hybriel master 73267707 (#122 fixed); compactNow workaround removed (#110 covered)mre
  • 2ab91ee9tickets: gate checks rows appear once (session sync); re-vendor to ff51cf46 stopped on hybriel#122, stays 837fe120mre
  • e01c2b1dtickets#24: installable app (manifest, service worker, offline list), own icon; gate waits for the hello's pongmre
  • 752fbb7fdeploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
  • 38bdd5e4deploy.sh: never send .git or .gitignore to Byrodinmre
  • f12fa1bcState of 2026-09-27, before the move to gitoriamre