diff --git a/.github/gbp.toml b/.github/gbp.toml new file mode 100644 index 0000000000..6eb7ece4f0 --- /dev/null +++ b/.github/gbp.toml @@ -0,0 +1,23 @@ +no_balance_label = "GBP: No Update" +reset_label = "GBP: Reset" + +[points] +"Accessibility" = 3 +"Administration" = 2 +"Balance" = -5 +"Code Improvement" = 2 +"Documentation" = 1 +"Feature" = -6 +"Good First PR" = 6 +"Fix" = 3 +"Grammar and Formatting" = 1 +"Hard Deletes" = 12 +"Logging" = 1 +"Sev: 0" = 20 +"Sev: 1-Blocker" = 20 +"Sev: 2-High" = 15 +"Quality of Life" = 1 +"Refactor" = 10 +"Sound" = 3 +"Sprites" = 3 +"Unit Tests" = 6 diff --git a/.github/workflows/autowiki.yml b/.github/workflows/autowiki.yml new file mode 100644 index 0000000000..2f6cadbbf9 --- /dev/null +++ b/.github/workflows/autowiki.yml @@ -0,0 +1,45 @@ +name: Autowiki +on: + schedule: + - cron: "5 4 * * *" + workflow_dispatch: +permissions: + contents: read + +jobs: + autowiki: + runs-on: ubuntu-latest + steps: + - name: "Check for AUTOWIKI_USERNAME" + id: secrets_set + env: + ENABLER_SECRET: ${{ secrets.AUTOWIKI_USERNAME }} + run: | + unset SECRET_EXISTS + if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi + echo "SECRETS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT + - name: Checkout + if: steps.secrets_set.outputs.SECRETS_ENABLED + uses: actions/checkout@v5 + - name: Install BYOND + if: steps.secrets_set.outputs.SECRETS_ENABLED + uses: ./.github/actions/restore_or_install_byond + - name: Install rust-g + if: steps.secrets_set.outputs.SECRETS_ENABLED + run: | + bash tools/ci/install_rust_g.sh + - name: Compile and generate Autowiki files + if: steps.secrets_set.outputs.SECRETS_ENABLED + run: | + source $HOME/BYOND/byond/bin/byondsetup + tools/build/build.sh --ci autowiki + - name: Run Autowiki + if: steps.secrets_set.outputs.SECRETS_ENABLED + env: + USERNAME: ${{ secrets.AUTOWIKI_USERNAME }} + PASSWORD: ${{ secrets.AUTOWIKI_PASSWORD }} + run: | + cd tools/autowiki + npm install + cd ../.. + node tools/autowiki/autowiki.js data/autowiki_edits.txt data/autowiki_files/ diff --git a/.github/workflows/gbp.yml b/.github/workflows/gbp.yml new file mode 100644 index 0000000000..7e9bacd8ea --- /dev/null +++ b/.github/workflows/gbp.yml @@ -0,0 +1,66 @@ +name: Label and GBP +on: + pull_request_target: + types: [closed, opened, synchronize] +jobs: + # labeler must run before gbp because gbp calculates itself based on labels + labeler: + runs-on: ubuntu-latest + if: github.event.action == 'opened' || github.event.action == 'synchronize' + permissions: + pull-requests: write # to apply labels + issues: write # to apply labels + steps: + - name: Checkout + uses: actions/checkout@v5 + - name: Run Auto Labeler + uses: actions/github-script@v8 + with: + script: | + const { get_updated_label_set } = await import('${{ github.workspace }}/tools/pull_request_hooks/autoLabel.js'); + const new_labels = await get_updated_label_set({ github, context }); + github.rest.issues.setLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: new_labels, + }); + console.log(`Labels updated: ${new_labels}`); + gbp: + runs-on: ubuntu-latest + if: github.event.action == 'opened' || github.event.action == 'closed' + steps: + - name: "Check for ACTION_ENABLER secret and pass true to output if it exists to be checked by later steps" + id: value_holder + env: + ENABLER_SECRET: ${{ secrets.ACTION_ENABLER }} + run: | + unset SECRET_EXISTS + if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi + echo "ACTIONS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT + - name: Checkout + if: steps.value_holder.outputs.ACTIONS_ENABLED + uses: actions/checkout@v5 + - name: Setup git + if: steps.value_holder.outputs.ACTIONS_ENABLED + run: | + git config --global user.name "gbp-action" + git config --global user.email "<>" + - name: Checkout alternate branch + if: steps.value_holder.outputs.ACTIONS_ENABLED + uses: actions/checkout@v5 + with: + ref: "gbp-balances" # The branch name + path: gbp-balances + # This is to ensure we keep the gbp.toml from master + # without having to update our separate branch. + - name: Copy configuration + if: steps.value_holder.outputs.ACTIONS_ENABLED + run: cp ./.github/gbp.toml ./gbp-balances/.github/gbp.toml + - name: GBP action + if: steps.value_holder.outputs.ACTIONS_ENABLED + uses: tgstation/gbp-action@master + with: + branch: "gbp-balances" + directory: ./gbp-balances + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/gbp_collect.yml b/.github/workflows/gbp_collect.yml new file mode 100644 index 0000000000..1e83e860ab --- /dev/null +++ b/.github/workflows/gbp_collect.yml @@ -0,0 +1,44 @@ +name: GBP Collection +# Every hour at the :20 minute mark. GitHub tells us to pick odd hours, instead of just using the start. +on: + schedule: + - cron: "20 * * * *" + workflow_dispatch: +jobs: + gbp_collection: + runs-on: ubuntu-latest + steps: + - name: "Check for ACTION_ENABLER secret and pass true to output if it exists to be checked by later steps" + id: value_holder + env: + ENABLER_SECRET: ${{ secrets.ACTION_ENABLER }} + run: | + unset SECRET_EXISTS + if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi + echo "ACTIONS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT + - name: Checkout + if: steps.value_holder.outputs.ACTIONS_ENABLED + uses: actions/checkout@v5 + - name: Setup git + if: steps.value_holder.outputs.ACTIONS_ENABLED + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + - name: Checkout alternate branch + if: steps.value_holder.outputs.ACTIONS_ENABLED + uses: actions/checkout@v5 + with: + ref: "gbp-balances" # The branch name + path: gbp-balances + # This is to ensure we keep the gbp.toml from master + # without having to update our separate branch. + - name: Copy configuration + if: steps.value_holder.outputs.ACTIONS_ENABLED + run: cp ./.github/gbp.toml ./gbp-balances/.github/gbp.toml + - name: GBP action + if: steps.value_holder.outputs.ACTIONS_ENABLED + uses: tgstation/gbp-action@master + with: + collect: "true" + directory: ./gbp-balances + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/code/_compile_options.dm b/code/_compile_options.dm index bc43d51fa2..872beb1c8a 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -3,5 +3,9 @@ #define USE_CUSTOM_ERROR_HANDLER #endif +/// If this is uncommented, Autowiki will generate edits and shut down the server. +/// Prefer the autowiki build target instead. +// #define AUTOWIKI + // We do not have dreamlua implemented #define DISABLE_DREAMLUAU diff --git a/code/modules/autowiki/autowiki.dm b/code/modules/autowiki/autowiki.dm new file mode 100644 index 0000000000..f7a7d957c3 --- /dev/null +++ b/code/modules/autowiki/autowiki.dm @@ -0,0 +1,33 @@ +/// When the `AUTOWIKI` define is enabled, will generate an output file for tools/autowiki/autowiki.js to consume. +/// Autowiki code intentionally still *exists* even without the define, to ensure developers notice +/// when they break it immediately, rather than until CI or worse, call time. +#if defined(AUTOWIKI) || defined(UNIT_TESTS) +/proc/setup_autowiki() + Master.sleep_offline_after_initializations = FALSE + SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(generate_autowiki))) + SSticker.start_immediately = TRUE + CONFIG_SET(number/round_end_countdown, 0) + +/proc/generate_autowiki() + var/output = generate_autowiki_output() + rustg_file_write(output, "data/autowiki_edits.txt") + qdel(world) +#endif + +/// Returns a string of the autowiki output file +/proc/generate_autowiki_output() + var/total_output = "" + + for (var/datum/autowiki/autowiki_type as anything in subtypesof(/datum/autowiki)) + var/datum/autowiki/autowiki = new autowiki_type + var/output = autowiki.generate() + + if (!istext(output)) + CRASH("[autowiki_type] does not generate a proper output!") + + total_output += json_encode(list( + "title" = autowiki.page, + "text" = output, + )) + "\n" + + return total_output diff --git a/code/modules/autowiki/page/base.dm b/code/modules/autowiki/page/base.dm new file mode 100644 index 0000000000..ce32bfd403 --- /dev/null +++ b/code/modules/autowiki/page/base.dm @@ -0,0 +1,59 @@ +/// A representation of an automated wiki page. +/datum/autowiki + /// The page on the wiki to be replaced. + /// This should never be a user-facing page, like "Guide to circuits". + /// It should always be a template that only Autowiki should touch. + /// For example: "Template:Autowiki/CircuitInfo". + var/page + +/// Override and return the new text of the page. +/// This proc can be impure, usually to call `upload_file`. +/datum/autowiki/proc/generate() + SHOULD_CALL_PARENT(FALSE) + CRASH("[type] does not implement generate()!") + +/// Generates an auto formatted template user. +/// Your autowiki should ideally be a *lot* of these. +/// It lets wiki editors edit it much easier later, without having to enter repo. +/// Parameters will be passed in by name. That means your template should expect +/// something that looks like `{{ Autowiki_Circuit|name=Combiner|description=This combines }}` +/// Lists, which must be array-like (no keys), will be turned into a flat list with their key and a number, +/// such that list("food" = list("fruit", "candy")) -> food1=fruit|food2=candy +/datum/autowiki/proc/include_template(name, parameters) + var/template_text = "{{[name]" + + var/list/prepared_parameters = list() + for (var/key in parameters) + var/value = parameters[key] + if (islist(value)) + for (var/index in 1 to length(value)) + prepared_parameters["[key][index]"] = "[value[index]]" + else + prepared_parameters[key] = value + + for (var/parameter_name in prepared_parameters) + template_text += "|[parameter_name]=" + template_text += "[prepared_parameters[parameter_name]]" + + template_text += "}}" + + return template_text + +/// Takes an icon and uploads it to Autowiki-name.png. +/// Do your best to make sure this is unique, so it doesn't clash with other autowiki icons. +/datum/autowiki/proc/upload_icon(icon/icon, name) + // Fuck you + if (IsAdminAdvancedProcCall()) + return + + var/static/uploaded_icons = list() + if(uploaded_icons["[name]"]) + CRASH("We tried uploading an icon, but the name \"[name]\" was already taken!") + + fcopy(icon, "data/autowiki_files/[name].png") + uploaded_icons["[name]"] = TRUE + +/// Escape a parameter such that it can be correctly put inside a wiki output +/datum/autowiki/proc/escape_value(parameter) + // | is a special character in MediaWiki, and must be escaped by...using another template. + return replacetextEx(parameter, "|", "{{!}}") diff --git a/code/modules/autowiki/page/symptom.dm b/code/modules/autowiki/page/symptom.dm new file mode 100644 index 0000000000..29aedd3210 --- /dev/null +++ b/code/modules/autowiki/page/symptom.dm @@ -0,0 +1,36 @@ +/datum/autowiki/symptom + page = "Template:Autowiki/Content/Symptoms" + +/datum/autowiki/symptom/generate() + var/output = "" + + var/list/template_list = list() + + for(var/the_symptom in subtypesof(/datum/symptom)) + var/datum/symptom/symptom = new the_symptom + + if(symptom.level < 0) // Skip base/admin symptoms + continue + + template_list["name"] = escape_value(symptom.name) + template_list["stealth"] = symptom.stealth + template_list["resistance"] = symptom.resistance + template_list["speed"] = symptom.stage_speed + template_list["transmission"] = symptom.transmission + template_list["level"] = symptom.level + template_list["effect"] = escape_value(symptom.desc) + template_list["thresholds"] = length(symptom.threshold_descs) ? generate_thresholds(symptom.threshold_descs) : "None" + + output += include_template("Autowiki/SymptomTemplate", template_list) + + return include_template("Autowiki/SymptomTableTemplate", list("content" = output)) + +/datum/autowiki/symptom/proc/generate_thresholds(var/list/thresholds) + var/compiled_thresholds = "" + + for(var/threshold in thresholds) + var/description = thresholds[threshold] + if(length(threshold)) + compiled_thresholds += "
  • [threshold]: [description]
  • " + + return compiled_thresholds diff --git a/code/modules/autowiki/page/techweb.dm b/code/modules/autowiki/page/techweb.dm new file mode 100644 index 0000000000..99b9766703 --- /dev/null +++ b/code/modules/autowiki/page/techweb.dm @@ -0,0 +1,68 @@ +/datum/autowiki/techweb + page = "Template:Autowiki/Content/Techweb" + +/datum/autowiki/techweb/generate() + var/output = "" + + for (var/node_id in sortList(SSresearch.techweb_nodes, GLOBAL_PROC_REF(sort_research_nodes))) + var/datum/techweb_node/node = SSresearch.techweb_nodes[node_id] + if (!node.show_on_wiki) + continue + + if (!valid_node(node)) + continue + + output += "\n\n" + include_template("Autowiki/TechwebEntry", list( + "name" = escape_value(node.display_name), + "description" = escape_value(node.description), + "prerequisites" = generate_prerequisites(node.prereq_ids), + "designs" = generate_designs(node.design_ids), + )) + + return output + +/datum/autowiki/techweb/proc/valid_node(datum/techweb_node/node) + return !node.experimental + +/datum/autowiki/techweb/proc/generate_designs(list/design_ids) + var/output = "" + + for (var/design_id in design_ids) + var/datum/design_techweb/design = SSresearch.techweb_designs[design_id] + output += include_template("Autowiki/TechwebEntryDesign", list( + "name" = escape_value(design.name), + "description" = escape_value(design.get_description()), + )) + + return output + +/datum/autowiki/techweb/proc/generate_prerequisites(list/prereq_ids) + var/output = "" + + for (var/prereq_id in prereq_ids) + var/datum/techweb_node/node = SSresearch.techweb_nodes[prereq_id] + output += include_template("Autowiki/TechwebEntryPrerequisite", list( + "name" = escape_value(node.display_name), + )) + + return output + +/datum/autowiki/techweb/experimental + page = "Template:Autowiki/Content/Techweb/Experimental" + +/datum/autowiki/techweb/experimental/valid_node(datum/techweb_node/node) + return node.experimental + +/proc/sort_research_nodes(node_id_a, node_id_b) + var/datum/techweb_node/node_a = SSresearch.techweb_nodes[node_id_a] + var/datum/techweb_node/node_b = SSresearch.techweb_nodes[node_id_b] + + var/prereq_difference = node_a.prereq_ids.len - node_b.prereq_ids.len + if (prereq_difference != 0) + return prereq_difference + + var/experiment_difference = node_a.required_experiments.len - node_b.required_experiments.len + if (experiment_difference != 0) + return experiment_difference + + return sorttext(node_b.display_name, node_a.display_name) diff --git a/code/modules/autowiki/page/vending.dm b/code/modules/autowiki/page/vending.dm new file mode 100644 index 0000000000..74dc575900 --- /dev/null +++ b/code/modules/autowiki/page/vending.dm @@ -0,0 +1,59 @@ +/datum/autowiki/vending + page = "Template:Autowiki/Content/VendingMachines" + +/datum/autowiki/vending/generate() + var/output = "" + + var/list/cached_products = list() + + // `powered()` checks if its in a null loc to say it's not powered. + // So we put it inside, something + var/obj/parent = new + + for (var/obj/machinery/vending/vending_type as anything in sortList(subtypesof(/obj/machinery/vending), GLOBAL_PROC_REF(cmp_typepaths_asc))) + var/obj/machinery/vending/parent_machine = type2parent(vending_type) + if(initial(parent_machine.name) == initial(vending_type.name)) + continue //Same name, likely just a slightly touched up subtype for specific maps. + var/obj/machinery/vending/vending_machine = new vending_type(parent) + vending_machine.use_power = FALSE + vending_machine.update_icon(UPDATE_ICON_STATE) + + // Technically won't match if product amounts change, but this isn't likely + var/products_cache_key = vending_machine.products.Join("-") + "&" + vending_machine.contraband.Join("-") + "&" + vending_machine.premium.Join("-") + + // In the future, this should show all vending machines that have the same products + if (products_cache_key in cached_products) + qdel(vending_machine) + continue + + cached_products += products_cache_key + + var/filename = SANITIZE_FILENAME(escape_value(format_text(vending_machine.name))) + + output += include_template("Autowiki/VendingMachine", list( + "icon" = escape_value(filename), + "name" = escape_value(format_text(vending_machine.name)), + "products" = format_product_list(vending_machine.products), + "contraband" = format_product_list(vending_machine.contraband), + "premium" = format_product_list(vending_machine.premium), + )) + + // It would be cool to make this support gifs someday, but not now + upload_icon(getFlatIcon(vending_machine, no_anim = TRUE), filename) + + qdel(vending_machine) + + qdel(parent) + + return output + +/datum/autowiki/vending/proc/format_product_list(list/product_list) + var/output = "" + + for (var/obj/product_path as anything in product_list) + output += include_template("Autowiki/VendingMachineProduct", list( + "name" = escape_value(capitalize(format_text(initial(product_path.name)))), + "amount" = product_list[product_path], + )) + + return output diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 9594732b55..1869a4b56c 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -88,6 +88,7 @@ // BEGIN_INCLUDE #include "asset_smart_cache.dm" +#include "autowiki.dm" //#include "clothing_tests.dm" // FIXME #include "component_tests.dm" #include "cosmetic_tests.dm" diff --git a/code/modules/unit_tests/autowiki.dm b/code/modules/unit_tests/autowiki.dm new file mode 100644 index 0000000000..65ec2e228d --- /dev/null +++ b/code/modules/unit_tests/autowiki.dm @@ -0,0 +1,35 @@ +/// Tests that all autowikis generate something without runtiming +/datum/unit_test/autowiki + +/datum/unit_test/autowiki/Run() + TEST_ASSERT(istext(generate_autowiki_output()), "generate_autowiki_output() did not finish successfully!") + +/// Test that `include_template` produces reasonable results +/datum/unit_test/autowiki_include_template + +/datum/unit_test/autowiki_include_template/Run() + var/datum/autowiki/autowiki_api = new + + TEST_ASSERT_EQUAL( \ + autowiki_api.include_template("Template"), \ + "{{Template}}", \ + "Basic template did not format correctly" \ + ) + + TEST_ASSERT_EQUAL( \ + autowiki_api.include_template("Template", list("name" = "Mothblocks")), \ + "{{Template|name=Mothblocks}}", \ + "Template with basic arguments did not format correctly" \ + ) + + TEST_ASSERT_EQUAL( \ + autowiki_api.include_template("Template", list("name" = autowiki_api.escape_value("P|peline"))), \ + "{{Template|name=P{{!}}peline}}", \ + "Template with escaped arguments did not format correctly" \ + ) + + TEST_ASSERT_EQUAL( \ + autowiki_api.include_template("Template", list("food" = list("fruit", "candy"))), \ + "{{Template|food1=fruit|food2=candy}}", \ + "Template with array arguments did not format correctly" \ + ) diff --git a/tools/autowiki/autowiki.js b/tools/autowiki/autowiki.js new file mode 100644 index 0000000000..fc74c58a0e --- /dev/null +++ b/tools/autowiki/autowiki.js @@ -0,0 +1,85 @@ +const fs = require('fs').promises; +const MWBot = require('mwbot'); + +const { USERNAME, PASSWORD } = process.env; + +if (!USERNAME) { + console.error('USERNAME was not set.'); + process.exit(1); +} + +if (!PASSWORD) { + console.error('PASSWORD was not set.'); + process.exit(1); +} + +const PAGE_EDIT_FILENAME = process.argv[2]; + +if (!PAGE_EDIT_FILENAME) { + console.error('No filename specified to edit pages'); + process.exit(1); +} + +const FILE_EDIT_FILENAME = process.argv[3]; + +if (!FILE_EDIT_FILENAME) { + console.error('No filename specified to edit files'); + process.exit(1); +} + +async function main() { + console.log(`Reading from ${PAGE_EDIT_FILENAME}`); + const editFile = await (await fs.readFile(PAGE_EDIT_FILENAME, 'utf8')).split( + '\n', + ); + + console.log(`Logging in as ${USERNAME}`); + + const bot = new MWBot(); + + await bot.loginGetEditToken({ + apiUrl: 'https://wiki.chompstation13.net/api.php', + username: USERNAME, + password: PASSWORD, + }); + + console.log('Logged in'); + + // This is not Promise.all as to not flood with a bunch of traffic at once + for (const editLine of editFile) { + if (editLine.length === 0) { + continue; + } + + let { title, text } = JSON.parse(editLine); + text = + 'This page is automated by Autowiki. Do NOT edit it manually.' + + text; + + console.log(`Editing ${title}...`); + await bot.edit(title, text, `Autowiki edit @ ${new Date().toISOString()}`); + } + + // Same here + for (const asset of await fs.readdir(FILE_EDIT_FILENAME)) { + const assetPath = `${FILE_EDIT_FILENAME}/${asset}`; + const assetName = `Autowiki-${asset}`; + + console.log(`Replacing ${assetName}...`); + await bot + .upload( + assetName, + assetPath, + `Autowiki upload @ ${new Date().toISOString()}`, + ) + .catch((error) => { + if (error.code === 'fileexists-no-change') { + console.log(`${assetName} is an exact duplicate`); + } else { + return Promise.reject(error); + } + }); + } +} + +main().catch(console.error); diff --git a/tools/autowiki/package.json b/tools/autowiki/package.json new file mode 100644 index 0000000000..281ab8fc82 --- /dev/null +++ b/tools/autowiki/package.json @@ -0,0 +1,10 @@ +{ + "name": "autowiki", + "version": "1.0.0", + "description": "Automatically publish generated pages to the virgo wiki", + "main": "autowiki.js", + "author": "Mothblocks", + "dependencies": { + "mwbot": "^2.0.0" + } +} diff --git a/tools/pull_request_hooks/autoLabel.test.js b/tools/pull_request_hooks/autoLabel.test.js new file mode 100644 index 0000000000..3d85af595b --- /dev/null +++ b/tools/pull_request_hooks/autoLabel.test.js @@ -0,0 +1,59 @@ +import { strict as assert } from 'node:assert'; +import { get_updated_label_set } from './autoLabel.js'; + +const empty_pr = { + action: 'opened', + pull_request: { + body: 'This PR will have no labels', + title: 'Pr with no labels', + mergeable: true, + }, +}; +const empty_label_set = await get_updated_label_set({ + github: null, + context: { payload: empty_pr }, +}); +assert.equal(empty_label_set.length, 0, 'No labels should be added'); + +const cl = ` +My Awesome PR + +:cl: Awesome Dude +add: Adds Awesome Stuff +refactor: refactored some code +:/cl: +`; +const cl_pr = { + action: 'opened', + pull_request: { + body: cl, + title: 'Awesome PR', + mergeable: false, + }, +}; +const cl_label_set = await get_updated_label_set({ + github: null, + context: { payload: cl_pr }, +}); +assert.ok( + cl_label_set.includes('Merge Conflict'), + 'Merge Conflict label should be added', +); +assert.ok(cl_label_set.includes('Feature'), 'Feature label should be added'); +assert.ok( + !cl_label_set.includes('Refactor'), + 'Refactor label should not be added', +); + +const title_pr = { + action: 'opened', + pull_request: { + title: 'Logging is important', + mergeable: true, + }, +}; +const title_label_set = await get_updated_label_set({ + github: null, + context: { payload: title_pr }, +}); +assert.ok(title_label_set.includes('Logging'), 'Logging label should be added'); diff --git a/vorestation.dme b/vorestation.dme index 97d089fa21..c872d50730 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -2283,6 +2283,10 @@ #include "code\modules\asset_cache\iconforge\universal_icon.dm" #include "code\modules\asset_cache\transports\asset_transport.dm" #include "code\modules\asset_cache\transports\webroot_transport.dm" +#include "code\modules\autowiki\autowiki.dm" +#include "code\modules\autowiki\page\base.dm" +#include "code\modules\autowiki\page\symptom.dm" +#include "code\modules\autowiki\page\techweb.dm" #include "code\modules\awaymissions\bluespaceartillery.dm" #include "code\modules\awaymissions\corpse.dm" #include "code\modules\awaymissions\exile.dm"